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.
/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: Connectivity.java
* Module to do node extraction (extract connectivity from a pure-layout cell)
* Written by Steven M. Rubin, Sun Microsystems.
*
* 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.ERectangle;
import com.sun.electric.database.geometry.GeometryHandler;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.database.geometry.PolyBase;
import com.sun.electric.database.geometry.PolyMerge;
import com.sun.electric.database.geometry.PolySweepMerge;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Export;
import com.sun.electric.database.network.Netlist;
import com.sun.electric.database.network.Network;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.text.Name;
import com.sun.electric.database.text.TextUtils;
import com.sun.electric.database.topology.ArcInst;
import com.sun.electric.database.topology.Connection;
import com.sun.electric.database.topology.Geometric;
import com.sun.electric.database.topology.HeadConnection;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.PortInst;
import com.sun.electric.database.topology.RTBounds;
import com.sun.electric.database.topology.RTNode;
import com.sun.electric.database.variable.DisplayedText;
import com.sun.electric.database.variable.EditWindow_;
import com.sun.electric.database.variable.ElectricObject;
import com.sun.electric.database.variable.UserInterface;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.Layer;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.PrimitivePort;
import com.sun.electric.technology.SizeOffset;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.Technology.NodeLayer;
import com.sun.electric.technology.Technology.TechPoint;
import com.sun.electric.technology.technologies.Generic;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.JobException;
import com.sun.electric.tool.routing.AutoStitch;
import com.sun.electric.tool.routing.AutoStitch.AutoOptions;
import com.sun.electric.tool.user.ErrorLogger;
import com.sun.electric.tool.user.Highlight;
import com.sun.electric.tool.user.User;
import com.sun.electric.tool.user.dialogs.EDialog;
import com.sun.electric.tool.user.ui.TopLevel;
import com.sun.electric.util.math.DBMath;
import com.sun.electric.util.math.ECoord;
import com.sun.electric.util.math.EDimension;
import com.sun.electric.util.math.FixpRectangle;
import com.sun.electric.util.math.FixpTransform;
import com.sun.electric.util.math.GenMath;
import com.sun.electric.util.math.MutableBoolean;
import com.sun.electric.util.math.MutableInteger;
import com.sun.electric.util.math.Orientation;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
/**
* This is the Connectivity extractor.
*
* Still need to handle non-Manhattan contacts
*/
public class Connectivity
{
/** true to prevent objects smaller than minimum size */ private static final boolean ENFORCEMINIMUMSIZE = false;
/** true to debug centerline determination */ private static final boolean DEBUGCENTERLINES = false;
/** true to debug object creation */ private static final boolean DEBUGSTEPS = false;
/** true to debug contact extraction */ private static final boolean DEBUGCONTACTS = false;
/** amount to scale values before merging */ private static final double SCALEFACTOR = DBMath.GRID;
/** the current technology for extraction */ private Technology tech;
/** layers to use for given arc functions */ private Map layerForFunction;
/** the layer to use for "polysilicon" geometry */ private Layer polyLayer;
/** temporary layers to use for geometric manipulation */ private Layer tempLayer1;
/** the layers to use for "active" geometry */ private Layer activeLayer, pActiveLayer, nActiveLayer;
/** the layers to use for "select" geometry */ private Layer pSelectLayer, nSelectLayer;
/** the real "active" layers */ private Layer realPActiveLayer, realNActiveLayer;
/** the well and substrate layers */ private Layer wellLayer, substrateLayer;
/** associates arc prototypes with layers */ private Map arcsForLayer;
/** map of extracted cells */ private Map convertedCells;
/** map of cut layers to lists of polygons on that layer */ private Map allCutLayers;
/** set of pure-layer nodes that are not processed */ private Set ignoreNodes;
/** set of contacts that are not used for extraction */ private Set bogusContacts;
/** PrimitiveNodes for p-diffusion and n-diffusion */ private PrimitiveNode diffNode, pDiffNode, nDiffNode;
/** list of Exports to restore after extraction */ private List exportsToRestore;
/** auto-generated exports that may need better names */ private List generatedExports;
/** true if this is a P-well process (presume P-well) */ private boolean pSubstrateProcess;
/** true if this is a N-well process (presume N-well) */ private boolean nSubstrateProcess;
/** helper variables for computing N/P process factors */ private boolean hasWell, hasPWell, hasNWell;
/** true to unify N and P active layers */ private boolean unifyActive;
/** helper variables for computing N and P active unify */ private boolean haveNActive, havePActive;
/** true to ignore select/well around active layers */ private boolean ignoreActiveSelectWell;
/** true to grid align the extracted geometry */ private boolean gridAlignExtraction;
/** true to approximate cut placement */ private boolean approximateCuts;
/** true if extracting hierarchically */ private boolean recursive;
/** the smallest polygon acceptable for merging */ private double smallestPoly;
/** debugging: list of objects created */ private List addedRectangles;
/** debugging: list of objects created */ private List addedLines;
/** list of exported pins to realize at the end */ private List pinsForLater;
/** ErrorLogger to keep up with errors during extraction */ private ErrorLogger errorLogger;
/** total number of cells to extract when recursing */ private int totalCells;
/** total number of cells extracted when recursing */ private int cellsExtracted;
/** Job that is holding the process */ private Job job;
/** EditingPreferences */ private EditingPreferences ep;
/** Grid alignment for edges */ private EDimension alignment;
/**
* Method to examine the current cell and extract it's connectivity in a new one.
* @param recursive true to recursively extract the hierarchy below this cell.
*/
public static void extractCurCell(boolean recursive)
{
Cell curCell = Job.getUserInterface().needCurrentCell();
if (curCell == null)
{
System.out.println("Must be editing a cell with pure layer nodes.");
return;
}
new ExtractJob(curCell, recursive);
}
private static class ExtractJob extends Job
{
private Cell cell, newCell;
private boolean recursive;
private double smallestPolygonSize;
private int activeHandling;
private String expansionPattern;
private boolean gridAlignExtraction;
private final ECoord scaledResolution;
private boolean approximateCuts;
private boolean flattenPcells;
private boolean usePureLayerNodes;
/** debugging: list of objects created */ private List> addedBatchRectangles;
/** debugging: list of objects created */ private List> addedBatchLines;
/** debugging: list of objects created */ private List addedBatchNames;
/** */ private ErrorLogger errorLogger;
private ExtractJob(Cell cell, boolean recursive)
{
super("Extract Connectivity from " + cell, Extract.getExtractTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.cell = cell;
this.recursive = recursive;
this.errorLogger = ErrorLogger.newInstance("Extraction Tool on cell " + cell.getName());
smallestPolygonSize = Extract.isIgnoreTinyPolygons() ? Extract.getSmallestPolygonSize() : 0;
activeHandling = Extract.getActiveHandling();
expansionPattern = Extract.getCellExpandPattern().trim();
gridAlignExtraction = Extract.isGridAlignExtraction();
Technology tech = cell.getTechnology();
scaledResolution = tech.getFactoryResolution();
// scaledResolution = tech.getFactoryScaledResolution();
// scaledResolution = new DRC.DRCPreferences(false).getResolution(tech);
approximateCuts = Extract.isApproximateCuts();
flattenPcells = Extract.isFlattenPcells();
usePureLayerNodes = Extract.isUsePureLayerNodes();
startJob();
}
public boolean doIt() throws JobException
{
// get pattern for matching cells to expand
List pats = new ArrayList();
if (expansionPattern.length() > 0)
{
String [] patParts = expansionPattern.split(",");
for(int i=0; i>();
addedBatchLines = new ArrayList>();
addedBatchNames = new ArrayList();
}
Job.getUserInterface().startProgressDialog("Extracting", null);
Connectivity c = new Connectivity(cell, this, errorLogger, smallestPolygonSize, activeHandling,
gridAlignExtraction, scaledResolution, approximateCuts, recursive, pats);
if (recursive) c.totalCells = c.countExtracted(cell, pats, flattenPcells);
c.cellsExtracted = 0;
newCell = c.doExtract(cell, recursive, pats, flattenPcells, usePureLayerNodes,
true, this, addedBatchRectangles, addedBatchLines, addedBatchNames);
if (newCell == null)
System.out.println("ERROR: Extraction of cell " + cell.describe(false) + " failed");
Job.getUserInterface().stopProgressDialog();
fieldVariableChanged("addedBatchRectangles");
fieldVariableChanged("addedBatchLines");
fieldVariableChanged("addedBatchNames");
fieldVariableChanged("newCell");
fieldVariableChanged("errorLogger");
return true;
}
public void terminateOK()
{
UserInterface ui = Job.getUserInterface();
EditWindow_ wnd = ui.displayCell(newCell);
Job.getUserInterface().termLogging(errorLogger, false, false);
if (DEBUGSTEPS)
{
// show results of each step
JFrame jf = null;
if (!TopLevel.isMDIMode()) jf = TopLevel.getCurrentJFrame();
ShowExtraction theDialog = new ShowExtraction(jf, addedBatchRectangles, addedBatchLines, addedBatchNames);
theDialog.setVisible(true);
} else
{
// highlight pure layer nodes
if (newCell != null) // cell is null if job was aborted
{
for(Iterator it = newCell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
PrimitiveNode.Function fun = ni.getFunction();
if (fun == PrimitiveNode.Function.NODE)
wnd.addElectricObject(ni, newCell);
}
}
else
System.out.println("Extraction job was aborted");
}
}
}
/**
* Constructor to initialize connectivity extraction.
* @param cell the cell
* @param j the job
* @param eLog the error logger
* @param smallestPolygonSize the smallest polygon size
* @param activeHandling
* 0: Insist on two different active layers (N and P) and also proper select/well surrounds (the default).
* 1: Ignore active distinctions and use select/well surrounds to distinguish N from P.
* 2: Insist on two different active layers (N and P) but ignore select/well surrounds.
* @param gridAlignExtraction true to align extraction to some the technology grid
* @param approximateCuts approximate cuts
* @param recursive run recursively
* @param pats a List of cell name patterns that will be flattened.
*/
private Connectivity(Cell cell, Job j, ErrorLogger eLog, double smallestPolygonSize, int activeHandling,
boolean gridAlignExtraction, ECoord scaledResolution, boolean approximateCuts, boolean recursive,
List pats)
{
this.approximateCuts = approximateCuts;
this.recursive = recursive;
tech = cell.getTechnology();
convertedCells = new HashMap();
smallestPoly = (SCALEFACTOR * SCALEFACTOR) * smallestPolygonSize;
bogusContacts = new HashSet();
errorLogger = eLog;
job = j;
ep = job.getEditingPreferences();
this.gridAlignExtraction = gridAlignExtraction;
alignment = new EDimension(scaledResolution, scaledResolution);
diffNode = pDiffNode = nDiffNode = null;
// find pure-layer nodes that are never involved in higher-level components, and should be ignored
ignoreNodes = new HashSet();
for(Iterator pIt = tech.getNodes(); pIt.hasNext(); )
{
PrimitiveNode np = pIt.next();
if (np.getFunction() != PrimitiveNode.Function.NODE) continue;
Technology.NodeLayer [] nLays = np.getNodeLayers();
boolean validLayers = false;
for(int i=0; i it = tech.getLayers(); it.hasNext(); )
{
Layer layer = it.next();
Layer.Function fun = layer.getFunction();
if (polyLayer == null && fun == Layer.Function.POLY1) polyLayer = layer;
if (activeLayer == null && fun == Layer.Function.DIFF) activeLayer = layer;
if (pActiveLayer == null && fun == Layer.Function.DIFFP) pActiveLayer = layer;
if (nActiveLayer == null && fun == Layer.Function.DIFFN) nActiveLayer = layer;
if (realPActiveLayer == null && fun == Layer.Function.DIFFP) realPActiveLayer = layer;
if (realNActiveLayer == null && fun == Layer.Function.DIFFN) realNActiveLayer = layer;
if (pSelectLayer == null && fun == Layer.Function.IMPLANTP) pSelectLayer = layer;
if (nSelectLayer == null && fun == Layer.Function.IMPLANTN) nSelectLayer = layer;
if (pSubstrateProcess) // p-substrate
{
if (wellLayer == null && fun == Layer.Function.WELLN) wellLayer = layer;
if (substrateLayer == null && fun == Layer.Function.WELLP) substrateLayer = layer;
}
if (nSubstrateProcess) // n-substrate
{
if (wellLayer == null && fun == Layer.Function.WELLP) wellLayer = layer;
if (substrateLayer == null && fun == Layer.Function.WELLN) substrateLayer = layer;
}
}
polyLayer = polyLayer.getNonPseudoLayer();
if (polyLayer != null)
tempLayer1 = polyLayer.getPseudoLayer();
if (havePActive != haveNActive)
{
// only one active layer found: force ignorance of the distinction
if (activeHandling != 1)
System.out.println("Found only one type of active layer: ignoring N/P distinction.");
activeHandling = 1;
if (!haveNActive) nActiveLayer = pActiveLayer;
if (!havePActive) pActiveLayer = nActiveLayer;
}
unifyActive = false;
if (activeHandling == 1)
{
// ignoring n/p distinction in active handling
unifyActive = true;
}
if ((pActiveLayer == null || nActiveLayer == null) && activeLayer != null)
{
// technology has only one active layer: unify them
unifyActive = true;
pActiveLayer = nActiveLayer = activeLayer;
}
if (pActiveLayer != null) pActiveLayer = pActiveLayer.getNonPseudoLayer();
if (nActiveLayer != null) nActiveLayer = nActiveLayer.getNonPseudoLayer();
// figure out which arcs to use for a layer
arcsForLayer = new HashMap();
for(Iterator it = tech.getLayers(); it.hasNext(); )
{
Layer layer = it.next();
Layer.Function fun = layer.getFunction();
if (fun.isDiff() || fun.isPoly() || fun.isMetal())
{
ArcProto.Function oFun = null;
if (fun.isMetal()) oFun = ArcProto.Function.getMetal(fun.getLevel());
if (fun.isPoly()) oFun = ArcProto.Function.getPoly(fun.getLevel());
if (oFun == null) continue;
ArcProto type = null;
for(Iterator aIt = tech.getArcs(); aIt.hasNext(); )
{
ArcProto ap = aIt.next();
if (ap.getFunction() == oFun) { type = ap; break; }
}
if (type != null) arcsForLayer.put(layer, type);
}
}
// build the mapping from any layer to the proper ones for the geometric database
layerForFunction = new HashMap();
for(Iterator it = tech.getLayers(); it.hasNext(); )
{
Layer layer = it.next();
Layer.Function fun = layer.getFunction();
if (unifyActive)
{
if (fun == Layer.Function.DIFFP || fun == Layer.Function.DIFFN)
fun = Layer.Function.DIFF;
}
if (layerForFunction.get(fun) == null)
layerForFunction.put(fun, layer);
}
}
/**
* Method to log errors during node extraction.
*/
private void addErrorLog(Cell cell, String msg, EPoint... pList)
{
List pointList = new ArrayList();
for(EPoint p : pList)
pointList.add(p);
errorLogger.logMessage(msg, pointList, cell, -1, true);
System.out.println(msg);
}
private int countExtracted(Cell oldCell, List pats, boolean flattenPcells)
{
int numExtracted = 1;
for(Iterator it = oldCell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.isCellInstance())
{
Cell subCell = (Cell)ni.getProto();
// do not recurse if this subcell will be expanded
if (isCellFlattened(subCell, pats, flattenPcells)) continue;
Cell convertedCell = convertedCells.get(subCell);
if (convertedCell == null)
{
numExtracted += countExtracted(subCell, pats, flattenPcells);
}
}
}
return numExtracted;
}
/**
* Method to determine whether to flatten a cell.
* @param cell the cell in question.
* @param pats patterns of cells to be flattened.
* @param flattenPcells true if Cadence Pcells are to be flattened.
* @return true if the cell should be flattened.
*/
private boolean isCellFlattened(Cell cell, List pats, boolean flattenPcells)
{
// do not recurse if this subcell will be expanded
for(Pattern pat : pats)
{
Matcher mat = pat.matcher(cell.noLibDescribe());
if (mat.find()) return true;
}
if (flattenPcells)
{
String cellName = cell.noLibDescribe();
int twoDollar = cellName.lastIndexOf("$$");
if (twoDollar > 0)
{
String endPart = cellName.substring(twoDollar+2);
for(int i=0; i pats, boolean flattenPcells, boolean usePureLayerNodes,
boolean top, Job job, List> addedBatchRectangles, List> addedBatchLines, List addedBatchNames)
{
if (recursive)
{
// first see if subcells need to be converted
for(Iterator it = oldCell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.isCellInstance())
{
Cell subCell = (Cell)ni.getProto();
// do not recurse if this subcell will be expanded
if (isCellFlattened(subCell, pats, flattenPcells)) continue;
Cell convertedCell = convertedCells.get(subCell);
if (convertedCell == null)
{
Cell result = doExtract(subCell, recursive, pats, flattenPcells, usePureLayerNodes,
false, job, addedBatchRectangles, addedBatchLines, addedBatchNames);
if (result == null)
System.out.println("ERROR: Extraction of cell " + subCell.describe(false) + " failed");
}
}
}
}
// create the new version of the cell
String newCellName = oldCell.getName() + oldCell.getView().getAbbreviationExtension();
Cell newCell = Cell.makeInstance(ep, oldCell.getLibrary(), newCellName);
if (newCell == null)
{
System.out.println("Cannot create new cell: " + newCellName);
return null;
}
convertedCells.put(oldCell, newCell);
// create a merge for the geometry in the cell
PolyMerge merge = new PolyMerge();
PolyMerge selectMerge = new PolyMerge();
// convert the nodes
if (!startSection(oldCell, "Gathering geometry in " + oldCell + "...")) // HAS PROGRESS IN IT
return null; // aborted
Set expandedCells = new HashSet();
exportsToRestore = new ArrayList();
generatedExports = new ArrayList();
pinsForLater = new ArrayList();
allCutLayers = new TreeMap();
extractCell(oldCell, newCell, pats, flattenPcells, expandedCells, merge, selectMerge, GenMath.MATID, Orientation.IDENT);
if (expandedCells.size() > 0)
{
System.out.print("These cells were expanded:");
for(Cell c : expandedCells)
System.out.print(" " + c.describe(false));
System.out.println();
}
// now remember the original merge
PolyMerge originalMerge = new PolyMerge();
originalMerge.addMerge(merge, new FixpTransform());
// start by extracting vias
initDebugging();
if (!startSection(oldCell, "Extracting vias...")) return null; // aborted
if (!extractVias(merge, originalMerge, oldCell, newCell, usePureLayerNodes)) return null; // aborted
termDebugging(addedBatchRectangles, addedBatchLines, addedBatchNames, "Vias");
// now extract transistors
initDebugging();
if (!startSection(oldCell, "Extracting transistors...")) return null; // aborted
extractTransistors(merge, originalMerge, newCell, usePureLayerNodes);
termDebugging(addedBatchRectangles, addedBatchLines, addedBatchNames, "Transistors");
if (usePureLayerNodes) {
// dump back in original routing layers
if (!startSection(oldCell, "Adding in original routing layers...")) return null;
addInRoutingLayers(oldCell, newCell, merge, originalMerge, usePureLayerNodes);
} else {
// extend geometry that sticks out in space
/*
initDebugging();
if (!startSection(oldCell, "Extracting extensions...")) return null; // aborted
extendGeometry(merge, originalMerge, newCell, true, usePureLayerNodes);
termDebugging(addedBatchRectangles, addedBatchLines, addedBatchNames, "StickOuts");
*/
// look for wires and pins
initDebugging();
if (!startSection(oldCell, "Extracting wires...")) return null; // aborted
if (makeWires(merge, originalMerge, newCell, usePureLayerNodes)) return newCell;
termDebugging(addedBatchRectangles, addedBatchLines, addedBatchNames, "Wires");
// convert any geometry that connects two networks
initDebugging();
if (!startSection(oldCell, "Extracting connections...")) return null; // aborted
extendGeometry(merge, originalMerge, newCell, false, usePureLayerNodes);
termDebugging(addedBatchRectangles, addedBatchLines, addedBatchNames, "Bridges");
}
// dump any remaining layers back in as extra pure layer nodes
initDebugging();
if (!startSection(oldCell, "Extracting leftover geometry...")) return null; // aborted
convertAllGeometry(merge, originalMerge, newCell, usePureLayerNodes);
termDebugging(addedBatchRectangles, addedBatchLines, addedBatchNames, "Pures");
// reexport any that were there before
if (!startSection(oldCell, "Adding connecting wires...")) return null; // aborted
cleanupExports(oldCell, newCell);
// cleanup by auto-stitching
PolyMerge originalUnscaledMerge = new PolyMerge();
double shrinkage = 1.0 / SCALEFACTOR;
FixpTransform shrink = new FixpTransform(shrinkage, 0, 0, shrinkage, 0, 0);
originalUnscaledMerge.addMerge(originalMerge, shrink);
Set allArcs = null;
allArcs = new HashSet();
for(Iterator it = newCell.getArcs(); it.hasNext(); )
allArcs.add(it.next());
// make sure current arc is not universal arc, otherwise it makes the InteractiveRouter (used by AutoStitch) prefer that arc
if (User.getUserTool().getCurrentArcProto() == Generic.tech().universal_arc) {
User.getUserTool().setCurrentArcProto(newCell.getTechnology().getArcs().next());
}
// TODO: originalMerge passed to auto stitcher really needs to include subcell geometry too, in order
// for the auto-stitcher to know where it can place arcs. However, building and maintaining such a hash map
// might take up a lot of memory.
AutoOptions prefs = new AutoOptions(true);
prefs.createExports = true;
AutoStitch.runAutoStitch(newCell, null, null, job, originalUnscaledMerge, null, false, true, ep, prefs, !recursive, alignment);
// check all the arcs that auto-stitching added, and replace them by universal arcs if they are off-grid
if (alignment != null && (alignment.getWidth() > 0 || alignment.getHeight() > 0))
{
for(Iterator it = newCell.getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
if (allArcs.contains(ai)) continue;
Rectangle2D bounds = ai.getBounds();
long lX = (long)(bounds.getMinX() / alignment.getWidth());
long hX = (long)(bounds.getMaxX() / alignment.getWidth());
long lY = (long)(bounds.getMinY() / alignment.getHeight());
long hY = (long)(bounds.getMaxY() / alignment.getHeight());
if (lX * alignment.getWidth() != bounds.getMinX() ||
lY * alignment.getHeight() != bounds.getMinY() ||
hX * alignment.getWidth() != bounds.getMaxX() ||
hY * alignment.getHeight() != bounds.getMaxY())
{
// replace
Connection head = ai.getHead();
Connection tail = ai.getTail();
ArcInst newAi = ArcInst.makeInstanceBase(Generic.tech().universal_arc, ep, 0, head.getPortInst(), tail.getPortInst(),
head.getLocation(), tail.getLocation(), null);
if (newAi != null)
{
newAi.setHeadExtended(false);
newAi.setTailExtended(false);
ai.kill();
}
}
}
}
if (DEBUGSTEPS)
{
initDebugging();
for(Iterator it = newCell.getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
if (allArcs.contains(ai)) continue;
Poly arcPoly = ai.makeLambdaPoly(ai.getGridBaseWidth(), Poly.Type.CLOSED);
addedRectangles.add(ERectangle.fromLambda(arcPoly.getBounds2D()));
}
termDebugging(addedBatchRectangles, addedBatchLines, addedBatchNames, "Stitches");
}
System.out.println("Extraction done.");
cellsExtracted++;
if (recursive) Job.getUserInterface().setProgressValue(cellsExtracted * 100 / totalCells);
return newCell;
}
/**
* Method to start a new connection section.
* @param msg message to display in progress window
* @return False if the job is scheduled for abort or was aborted
*/
private boolean startSection(Cell cell, String msg)
{
System.out.println(msg);
if (job.checkAbort())
return false;
if (recursive) msg = cell.getName() + " - " + msg;
Job.getUserInterface().setProgressNote(msg);
if (!recursive) Job.getUserInterface().setProgressValue(0);
return true;
}
private void initDebugging()
{
if (DEBUGSTEPS)
{
addedRectangles = new ArrayList();
addedLines = new ArrayList();
}
}
private void termDebugging(List> addedBatchRectangles,
List> addedBatchLines, List addedBatchNames, String descr)
{
if (DEBUGSTEPS)
{
addedBatchRectangles.add(addedRectangles);
addedBatchLines.add(addedLines);
addedBatchNames.add(descr);
}
}
private static class ExportedPin
{
Point2D location;
NodeInst ni;
FixpTransform trans;
ExportedPin(NodeInst ni, Point2D location, FixpTransform trans)
{
this.ni = ni;
this.location = location;
this.trans = trans;
}
}
/**
* Method to extract a cell's contents into the merge.
* @param oldCell the cell being extracted.
* @param newCell the new cell being created.
* @param pats patterns of subcell names that will be expanded.
* @param flattenPcells true to expand Cadence Pcells (which end with $$number).
* @param expandedCells a set of cells that matched the pattern and were expanded.
* @param merge the merge to be filled.
* @param prevTrans the transformation coming into this cell.
*/
private void extractCell(Cell oldCell, Cell newCell, List pats, boolean flattenPcells, Set expandedCells,
PolyMerge merge, PolyMerge selectMerge, FixpTransform prevTrans, Orientation orient)
{
Map newNodes = new HashMap();
int totalNodes = oldCell.getNumNodes();
EDimension alignementToGrid = ep.getAlignmentToGrid();
// first get select, so we can determine proper active type
if (!unifyActive && !ignoreActiveSelectWell)
{
for (Iterator nIt = oldCell.getNodes(); nIt.hasNext(); )
{
NodeInst ni = nIt.next();
if (ni.isCellInstance()) continue;
Poly [] polys = tech.getShapeOfNode(ni);
for(int j=0; j nIt = oldCell.getNodes(); nIt.hasNext(); )
{
NodeInst ni = nIt.next();
soFar++;
if (!recursive && (soFar % 100) == 0) Job.getUserInterface().setProgressValue(soFar * 100 / totalNodes);
if (ni.getProto() == Generic.tech().cellCenterNode) continue;
// see if the node can be copied or must be extracted
NodeProto copyType = null;
if (ni.isCellInstance())
{
Cell subCell = (Cell)ni.getProto();
// if subcell is expanded, do it now
boolean flatIt = isCellFlattened(subCell, pats, flattenPcells);
if (flatIt)
{
// expanding the subcell
expandedCells.add(subCell);
FixpTransform subTrans = ni.translateOut(ni.rotateOut(prevTrans));
Orientation or = orient.concatenate(ni.getOrient());
extractCell(subCell, newCell, pats, flattenPcells, expandedCells, merge, selectMerge, subTrans, or);
continue;
}
// subcell not expanded, figure out what gets placed in the new cell
copyType = convertedCells.get(subCell);
if (copyType == null) copyType = subCell;
} else
{
PrimitiveNode np = (PrimitiveNode)ni.getProto();
// special case for exported but unconnected pins: save for later
if (np.getFunction().isPin())
{
if (ni.hasExports() && !ni.hasConnections())
{
ExportedPin ep = new ExportedPin(ni, ni.getTrueCenter(), prevTrans);
pinsForLater.add(ep);
continue;
}
}
if (np.getFunction() != PrimitiveNode.Function.NODE) copyType = ni.getProto(); else
{
if (ignoreNodes.contains(np)) copyType = ni.getProto();
}
}
// copy it now if requested
if (copyType != null)
{
double sX = ni.getXSize();
double sY = ni.getYSize();
if (copyType instanceof Cell)
{
Rectangle2D cellBounds = ((Cell)copyType).getBounds();
sX = cellBounds.getWidth();
sY = cellBounds.getHeight();
}
Point2D instanceAnchor = new Point2D.Double(0, 0);
prevTrans.transform(ni.getAnchorCenter(), instanceAnchor);
String name = null;
Name nameKey = ni.getNameKey();
if (!nameKey.isTempname()) name = ni.getName();
Orientation or = orient.concatenate(ni.getOrient());
if (name != null && newCell.findNode(name) != null) name = null;
NodeInst newNi = NodeInst.makeInstance(copyType, ep, instanceAnchor, sX, sY,
newCell, or, name, ni.getTechSpecific());
if (newNi == null)
{
addErrorLog(newCell, "Problem creating new instance of " + ni.getProto(), new EPoint(sX, sY));
continue;
}
newNodes.put(ni, newNi);
// copy exports too
for(Iterator it = ni.getExports(); it.hasNext(); )
{
Export e = it.next();
PortInst pi = newNi.findPortInstFromProto(e.getOriginalPort().getPortProto());
Export.newInstance(newCell, pi, e.getName(), ep);
}
continue;
}
// see if the size is at an odd coordinate (and may suffer rounding problems)
boolean growABit = false;
if (((int)(ni.getXSize() * DBMath.GRID) % 2) != 0 ||
((int)(ni.getYSize() * DBMath.GRID) % 2) != 0) growABit = true;
// extract the geometry from the pure-layer node
FixpTransform trans = ni.rotateOut(prevTrans);
Poly [] polys = tech.getShapeOfNode(ni);
for(int j=0; j sea = new RTNode.Search(poly.getBounds2D(), cInfo.getRTree(), true); sea.hasNext(); )
{
CutBound cBound = sea.next();
if (cBound.getBounds().equals(poly.getBounds2D())) { found = true; break; }
}
if (!found)
{
cInfo.addCut(poly);
}
} else
{
Rectangle2D box = poly.getBox();
if (box == null) merge.addPolygon(layer, poly); else
{
if (box.getWidth() > 0 && box.getHeight() > 0)
{
if (layer.getName().equals("DeviceMark"))
System.out.println("DEVICE MARK IS "+box.getWidth()+"x"+box.getHeight());
merge.addRectangle(layer, box);
}
}
}
}
// save exports on pure-layer nodes for restoration later
for(Iterator it = ni.getExports(); it.hasNext(); )
{
Export e = it.next();
exportsToRestore.add(e);
}
}
// throw all arcs into the new cell, too
for(Iterator aIt = oldCell.getArcs(); aIt.hasNext(); )
{
ArcInst ai = aIt.next();
NodeInst end1 = newNodes.get(ai.getHeadPortInst().getNodeInst());
NodeInst end2 = newNodes.get(ai.getTailPortInst().getNodeInst());
if (end1 == null || end2 == null) continue;
PortInst pi1 = end1.findPortInstFromProto(ai.getHeadPortInst().getPortProto());
PortInst pi2 = end2.findPortInstFromProto(ai.getTailPortInst().getPortProto());
Point2D headLocation = new Point2D.Double(0, 0);
Point2D tailLocation = new Point2D.Double(0, 0);
prevTrans.transform(ai.getHeadLocation(), headLocation);
prevTrans.transform(ai.getTailLocation(), tailLocation);
ArcInst.makeInstanceBase(ai.getProto(), ep, ai.getLambdaBaseWidth(), pi1, pi2,
headLocation, tailLocation, ai.getName());
}
// throw all cell text into the new cell, too
for(Iterator vIt = oldCell.getParametersAndVariables(); vIt.hasNext(); )
{
Variable var = vIt.next();
Variable newVar = Variable.newInstance(var.getKey(), var.getObject(), var.getTextDescriptor());
if (var.getTextDescriptor().isParam())
newCell.getCellGroup().addParam(newVar);
else
newCell.addVar(newVar);
new DisplayedText(newCell, var.getKey());
}
}
/**
* Method to determine if this is a "p-well" or "n-well" process.
* Examines the top-level cell to see which well layers are found.
* @param cell the top-level Cell.
* @param recursive true to examine recursively.
* @param pats exclusion pattern for cell names.
* @param activeHandling
* 0: Insist on two different active layers (N and P) and also proper select/well surrounds (the default).
* 1: Ignore active distinctions and use select/well surrounds to distinguish N from P.
* 2: Insist on two different active layers (N and P) but ignore select/well surrounds.
*/
private void findMissingWells(Cell cell, boolean recursive, List pats, int activeHandling)
{
hasWell = hasPWell = hasNWell = false;
haveNActive = havePActive = false;
recurseMissingWells(cell, recursive, pats);
if (!hasPWell)
{
pSubstrateProcess = true;
System.out.println("Presuming a P-substrate process");
} else if (!hasNWell && !hasWell)
{
nSubstrateProcess = true;
System.out.println("Presuming an N-substrate process");
}
// see how active layers should be handled
ignoreActiveSelectWell = (activeHandling == 2);
}
/**
* Method to recursively invoke "examineCellForMissingWells".
* @param cell the top-level Cell.
* @param recursive true to examine recursively.
* @param pats exclusion patterns for cell names.
*/
private void recurseMissingWells(Cell cell, boolean recursive, List pats)
{
examineCellForMissingWells(cell);
if (recursive)
{
// now see if subcells need to be converted
for(Iterator it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.isCellInstance())
{
Cell subCell = (Cell)ni.getProto();
// do not recurse if this subcell will be expanded
boolean matches = false;
for(Pattern pat : pats)
{
Matcher mat = pat.matcher(subCell.noLibDescribe());
if (mat.find()) { matches = true; break; }
}
if (matches) continue;
Cell convertedCell = convertedCells.get(subCell);
if (convertedCell == null)
{
recurseMissingWells(subCell, recursive, pats);
}
}
}
}
}
/**
* Method to scan a cell for implant layers that would indicate a default N or P process.
* @param cell the Cell to examine.
* Sets the field variables "hasWell", "hasPWell", and "hasNWell".
*/
private void examineCellForMissingWells(Cell cell)
{
for(Iterator it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.isCellInstance()) continue;
Poly[] polys = ni.getProto().getTechnology().getShapeOfNode(ni);
for(int i=0; i nIt = cell.getNodes(); nIt.hasNext(); )
{
NodeInst ni = nIt.next();
if (ni.isCellInstance()) continue;
PrimitiveNode np = (PrimitiveNode)ni.getProto();
if (np == Generic.tech().cellCenterNode) continue;
// see if the node will be extracted
PrimitiveNode copyType = null;
if (np.getFunction() != PrimitiveNode.Function.NODE) copyType = np; else
{
if (ignoreNodes.contains(np)) copyType = np;
}
// examine it if so
if (copyType != null)
{
NodeLayer [] nLayers = copyType.getNodeLayers();
for(int i=0; i aIt = cell.getArcs(); aIt.hasNext(); )
{
ArcInst ai = aIt.next();
for(int i=0; i> geomToWire = new TreeMap>();
for (Layer layer : merge.getKeySet())
{
Layer.Function fun = layer.getFunction();
if (fun.isDiff() || fun.isPoly() || fun.isMetal())
{
List polyList = getMergePolys(merge, layer, null);
totPolys += polyList.size();
geomToWire.put(layer, polyList);
}
}
// examine each wire layer, looking for a skeletal structure that approximates it
int soFar = 0;
Set allLayers = geomToWire.keySet();
for (Layer layer : allLayers)
{
// examine the geometry on the layer
List polyList = geomToWire.get(layer);
for(PolyBase poly : polyList)
{
if (!recursive) Job.getUserInterface().setProgressValue(soFar * 100 / totPolys);
soFar++;
// figure out which arcproto to use here
ArcProto ap = findArcProtoForPoly(layer, poly, originalMerge);
if (ap == null) continue;
// reduce the geometry to a skeleton of centerlines
double minWidth = 1;
if (ENFORCEMINIMUMSIZE) minWidth = scaleUp(ap.getDefaultLambdaBaseWidth(ep));
List lines = findCenterlines(poly, layer, minWidth, merge, originalMerge);
// now realize the wires
for(Centerline cl : lines)
{
ap = findArcProtoForPoly(layer, poly, originalMerge);
Point2D loc1Unscaled = new Point2D.Double();
Point2D loc2Unscaled = new Point2D.Double();
PortInst pi1 = locatePortOnCenterline(cl, loc1Unscaled, layer, ap, true, newCell);
Point2D loc1 = new Point2D.Double(scaleUp(loc1Unscaled.getX()), scaleUp(loc1Unscaled.getY()));
PortInst pi2 = locatePortOnCenterline(cl, loc2Unscaled, layer, ap, false, newCell);
Point2D loc2 = new Point2D.Double(scaleUp(loc2Unscaled.getX()), scaleUp(loc2Unscaled.getY()));
// make sure the wire fits
MutableBoolean headExtend = new MutableBoolean(true), tailExtend = new MutableBoolean(true);
// adjust extension to get alignment right
if (loc1.getX() == loc2.getX())
{
// vertical arc: adjust extension to make sure top and bottom are on grid
double loc1Y = loc1Unscaled.getY();
double loc2Y = loc2Unscaled.getY();
double halfWidth = cl.width/2/SCALEFACTOR;
double loc1YExtend = loc1Y + (loc1Y < loc2Y ? -halfWidth : halfWidth);
double loc2YExtend = loc2Y + (loc2Y < loc1Y ? -halfWidth : halfWidth);
if (!isOnGrid(loc1YExtend, alignment.getHeight()) && isOnGrid(loc1Y, alignment.getHeight()))
headExtend.setValue(false);
if (!isOnGrid(loc2YExtend, alignment.getHeight()) && isOnGrid(loc2Y, alignment.getHeight()))
tailExtend.setValue(false);
} else if (loc1.getY() == loc2.getY())
{
// horizontal arc: adjust extension to make sure left and right are on grid
double loc1X = loc1Unscaled.getX();
double loc2X = loc2Unscaled.getX();
double halfWidth = cl.width/2/SCALEFACTOR;
double loc1XExtend = loc1X + (loc1X < loc2X ? -halfWidth : halfWidth);
double loc2XExtend = loc2X + (loc2X < loc1X ? -halfWidth : halfWidth);
if (!isOnGrid(loc1XExtend, alignment.getWidth()) && isOnGrid(loc1X, alignment.getWidth()))
headExtend.setValue(false);
if (!isOnGrid(loc2XExtend, alignment.getWidth()) && isOnGrid(loc2X, alignment.getWidth()))
tailExtend.setValue(false);
}
boolean fits = originalMerge.arcPolyFits(layer, loc1, loc2, cl.width, headExtend, tailExtend);
if (DEBUGCENTERLINES) System.out.println("FIT="+fits+" "+cl);
if (!fits)
{
// arc does not fit, try reducing width
double wid = cl.width / SCALEFACTOR;
long x = Math.round(wid / alignment.getWidth());
double gridWid = x * alignment.getWidth();
if (gridWid < wid)
{
// grid-aligning the width results in a smaller value...try it
cl.width = scaleUp(gridWid);
fits = originalMerge.arcPolyFits(layer, loc1, loc2, cl.width, headExtend, tailExtend);
if (DEBUGCENTERLINES) System.out.println(" WID="+(cl.width/SCALEFACTOR)+" FIT="+fits);
} else
{
// see if width can be reduced by a small amount and still fit
cl.width--;
fits = originalMerge.arcPolyFits(layer, loc1, loc2, cl.width, headExtend, tailExtend);
if (DEBUGCENTERLINES) System.out.println(" WID="+(cl.width/SCALEFACTOR)+" FIT="+fits);
}
}
while (!fits)
{
double wid = cl.width - SCALEFACTOR;
if (wid < 0) break;
cl.width = wid;
fits = originalMerge.arcPolyFits(layer, loc1, loc2, cl.width, headExtend, tailExtend);
if (DEBUGCENTERLINES) System.out.println(" WID="+(cl.width/SCALEFACTOR)+" FIT="+fits);
}
if (!fits || (loc1Unscaled.distance(loc2Unscaled) == 0 && !headExtend.booleanValue() && !tailExtend.booleanValue()))
{
cl.width = 0;
ap = Generic.tech().universal_arc;
}
if (loc1Unscaled.distance(loc2Unscaled) == 0 && !headExtend.booleanValue() && !tailExtend.booleanValue()) {
//System.out.println("zero length arc in make wires");
}
// create the wire
ArcInst ai = realizeArc(ap, pi1, pi2, loc1Unscaled, loc2Unscaled, cl.width / SCALEFACTOR,
!headExtend.booleanValue(), !tailExtend.booleanValue(), usePureLayerNodes, merge);
if (ai == null)
{
String msg = "Cell " + newCell.describe(false) + ": Failed to run arc " + ap.getName() +
" from (" + loc1Unscaled.getX() + "," +
loc1Unscaled.getY() + ") on node " + pi1.getNodeInst().describe(false) + " to (" +
loc2Unscaled.getX() + "," + loc2Unscaled.getY() + ") on node " + pi2.getNodeInst().describe(false);
addErrorLog(newCell, msg, new EPoint(loc1Unscaled.getX(), loc1Unscaled.getY()),
new EPoint(loc2Unscaled.getX(), loc2Unscaled.getY()));
}
}
}
}
// add in pure layer node for remaining geometrics
for (Layer layer : allLayers)
{
List polyList = getMergePolys(merge, layer, null);
for(PolyBase poly : polyList)
{
ArcProto ap = findArcProtoForPoly(layer, poly, originalMerge);
if (ap == null) continue;
PrimitiveNode pin = ap.findPinProto();
List niList = makePureLayerNodeFromPoly(poly, newCell, merge);
merge.subtract(layer, poly);
// connect up to enclosed pins
for (NodeInst ni : niList)
{
PortInst fPi = ni.getOnlyPortInst();
Rectangle2D polyBounds = ni.getBounds();
Rectangle2D searchBound = new Rectangle2D.Double(polyBounds.getMinX(), polyBounds.getMinY(),
polyBounds.getWidth(), polyBounds.getHeight());
for(Iterator it = newCell.searchIterator(searchBound); it.hasNext(); )
{
Geometric geom = it.next();
if (!(geom instanceof NodeInst)) continue;
NodeInst oNi = (NodeInst)geom;
if (oNi == ni) continue;
if (oNi.getProto() != pin) continue;
// make sure center of pin is in bounds
if (!DBMath.pointInsideRect(oNi.getAnchorCenter(), searchBound)) continue;
// replace arcs that end on pin to end on pure layer node
for (Iterator cit = oNi.getConnections(); cit.hasNext(); ) {
Connection conn = cit.next();
Connection oConn;
ArcInst ai = conn.getArc();
if (ai.getProto() == Generic.tech().universal_arc) continue;
ArcInst newAi;
if (conn instanceof HeadConnection) {
oConn = ai.getTail();
newAi = ArcInst.makeInstanceBase(ap, ep, ai.getLambdaBaseWidth(), fPi, oConn.getPortInst(),
conn.getLocation(), oConn.getLocation(), null);
} else {
oConn = ai.getHead();
newAi = ArcInst.makeInstanceBase(ap, ep, ai.getLambdaBaseWidth(), oConn.getPortInst(), fPi,
oConn.getLocation(), conn.getLocation(), null);
}
if (newAi != null) {
newAi.setHeadExtended(ai.isHeadExtended());
newAi.setTailExtended(ai.isTailExtended());
if (newAi.getLambdaLength() == 0)
System.out.println("arc inst of zero length connecting pure layer nodes");
ai.kill();
} else {
String msg = "Cell " + newCell.describe(false) + ": Failed to replace arc " + ap.getName() +
" from (" + conn.getLocation().getX() + "," +
conn.getLocation().getY() + ") on node " + ni.describe(false) + " to (" +
oConn.getLocation().getX() + "," + oConn.getLocation().getY() + ")";
addErrorLog(newCell, msg, new EPoint(conn.getLocation().getX(), conn.getLocation().getY()),
new EPoint(oConn.getLocation().getX(), oConn.getLocation().getY()));
}
}
}
}
}
}
if (true) return false;
// examine each wire layer, looking for a simple rectangle that covers it
for(Layer layer : allLayers)
{
// examine the geometry on the layer
List polyList = getMergePolys(merge, layer, null);
for(PolyBase poly : polyList)
{
Rectangle2D bounds = poly.getBounds2D();
// make sure polygon is in the merge
Poly rectPoly = new Poly(bounds);
if (!originalMerge.contains(layer, rectPoly)) continue;
// grid align the edges of this rectangle
double lX = bounds.getMinX()/SCALEFACTOR, hX = bounds.getMaxX()/SCALEFACTOR;
double lY = bounds.getMinY()/SCALEFACTOR, hY = bounds.getMaxY()/SCALEFACTOR;
double alignX = alignment.getWidth();
double alignY = alignment.getHeight();
if (!isOnGrid(lX, alignX)) lX = Math.ceil(lX / alignX) * alignX;
if (!isOnGrid(hX, alignX)) hX = Math.floor(hX / alignX) * alignX;
if (!isOnGrid(lY, alignY)) lY = Math.ceil(lY / alignY) * alignY;
if (!isOnGrid(hY, alignY)) hY = Math.floor(hY / alignY) * alignY;
if (lX >= hX || lY >= hY) continue;
// grid align the center of this rectangle
/*
double cX = (lX + hX) / 2, cY = (lY + hY) / 2;
if (!isOnGrid(cX, alignX))
{
// try expanding to the right so center is aligned
double cXright = Math.ceil(cX / alignX) * alignX;
Poly testPoly = new Poly(cXright*SCALEFACTOR, cY*SCALEFACTOR, ((cXright-lX) * 2)*SCALEFACTOR, (hY-lY)*SCALEFACTOR);
if (originalMerge.contains(layer, testPoly)) bounds = testPoly.getBounds2D(); else
{
// try expanding to the left so center is aligned
double cXleft = Math.floor(cX / alignX) * alignX;
testPoly = new Poly(cXleft*SCALEFACTOR, cY*SCALEFACTOR, ((hX-cXleft) * 2)*SCALEFACTOR, (hY-lY)*SCALEFACTOR);
if (originalMerge.contains(layer, testPoly)) bounds = testPoly.getBounds2D(); else
{
// try contracting on the right so center is aligned
testPoly = new Poly(cXright*SCALEFACTOR, cY*SCALEFACTOR, ((hX-cXright) * 2)*SCALEFACTOR, (hY-lY)*SCALEFACTOR);
if (originalMerge.contains(layer, testPoly)) bounds = testPoly.getBounds2D(); else
{
// try contracting on the left so center is aligned
testPoly = new Poly(cXleft*SCALEFACTOR, cY*SCALEFACTOR, ((cXleft-lX) * 2)*SCALEFACTOR, (hY-lY)*SCALEFACTOR);
if (originalMerge.contains(layer, testPoly)) bounds = testPoly.getBounds2D(); else
continue;
}
}
}
if (bounds.getWidth() <= 0) continue;
lX = bounds.getMinX()/SCALEFACTOR;
hX = bounds.getMaxX()/SCALEFACTOR;
cX = (lX + hX) / 2;
}
if (!isOnGrid(cY, alignY))
{
// try expanding upward so center is aligned
double cYup = Math.ceil(cY / alignY) * alignY;
Poly testPoly = new Poly(cX*SCALEFACTOR, cYup*SCALEFACTOR, (hX-lX)*SCALEFACTOR, ((cYup-lY) * 2)*SCALEFACTOR);
if (originalMerge.contains(layer, testPoly)) bounds = testPoly.getBounds2D(); else
{
// try expanding downward so center is aligned
double cYdown = Math.floor(cY / alignY) * alignY;
testPoly = new Poly(cX*SCALEFACTOR, cYdown*SCALEFACTOR, (hX-lX)*SCALEFACTOR, ((hY-cYdown) * 2)*SCALEFACTOR);
if (originalMerge.contains(layer, testPoly)) bounds = testPoly.getBounds2D(); else
{
// try contracting upward so center is aligned
testPoly = new Poly(cX*SCALEFACTOR, cYup*SCALEFACTOR, (hX-lX)*SCALEFACTOR, ((hY-cYup) * 2)*SCALEFACTOR);
if (originalMerge.contains(layer, testPoly)) bounds = testPoly.getBounds2D(); else
{
// try contracting downward so center is aligned
testPoly = new Poly(cX*SCALEFACTOR, cYdown*SCALEFACTOR, (hX-lX)*SCALEFACTOR, ((cYdown-lY) * 2)*SCALEFACTOR);
if (originalMerge.contains(layer, testPoly)) bounds = testPoly.getBounds2D(); else
continue;
}
}
}
if (bounds.getHeight() <= 0) continue;
}
*/
// figure out which arc prototype to use for the layer
ArcProto ap = findArcProtoForPoly(layer, poly, originalMerge);
if (ap == null) continue;
// determine the endpoints of the arc
Point2D loc1, loc2;
double width;
if (bounds.getWidth() > bounds.getHeight())
{
// horizontal arc
width = bounds.getHeight();
loc1 = new Point2D.Double((bounds.getMinX()) / SCALEFACTOR, bounds.getCenterY() / SCALEFACTOR);
loc2 = new Point2D.Double((bounds.getMaxX()) / SCALEFACTOR, bounds.getCenterY() / SCALEFACTOR);
} else
{
// vertical arc
width = bounds.getWidth();
loc1 = new Point2D.Double(bounds.getCenterX() / SCALEFACTOR, (bounds.getMinY()) / SCALEFACTOR);
loc2 = new Point2D.Double(bounds.getCenterX() / SCALEFACTOR, (bounds.getMaxY()) / SCALEFACTOR);
}
PortInst pi1 = wantConnectingNodeAt(loc1, ap, width / SCALEFACTOR, newCell);
PortInst pi2 = wantConnectingNodeAt(loc2, ap, width / SCALEFACTOR, newCell);
realizeArc(ap, pi1, pi2, loc1, loc2, width / SCALEFACTOR, true, true, usePureLayerNodes, merge);
}
}
return false;
}
/**
* Choose the right type of active for the active in the merge (takes into account select)
*/
private void fixActiveNodes(Cell cell)
{
if (unifyActive) return; // no need to fix active
if (ignoreActiveSelectWell) return;
// if unifyActive is set, then Electric tech must only have one active
// Otherwise, imported GDS may only have one active, but Electric has two, so
// we need to fix that here.
PrimitiveNode pDiffNode = null, nDiffNode = null;
for (Iterator it = cell.getTechnology().getNodes(); it.hasNext(); )
{
PrimitiveNode pn = it.next();
if (pn.getFunction() == PrimitiveNode.Function.NODE) {
Layer layer = pn.getLayerIterator().next();
if (layer.getFunction() == Layer.Function.DIFFN)
nDiffNode = pn;
if (layer.getFunction() == Layer.Function.DIFFP)
pDiffNode = pn;
}
}
// first get all select layers
PolyMerge merge = new PolyMerge();
for (Iterator it = cell.getNodes(); it.hasNext(); ) {
NodeInst ni = it.next();
if (ni.isCellInstance()) continue;
Poly [] polys = tech.getShapeOfNode(ni);
for(int j=0; j it = cell.getNodes(); it.hasNext(); ) {
NodeInst ni = it.next();
if (ni.isCellInstance()) continue;
PrimitiveNode pn = (PrimitiveNode)ni.getProto();
if (pn.getFunction() == PrimitiveNode.Function.NODE) {
Layer nodeLayer = pn.getLayerIterator().next();
PrimitiveNode newType = null;
if (nodeLayer.getFunction() == Layer.Function.DIFFN) {
// make sure n-diffusion is in n-select
Poly [] polys = tech.getShapeOfNode(ni);
for (Poly poly : polys) {
if (merge.contains(pSelectLayer, poly)) {
// switch it p-active
newType = pDiffNode;
break;
}
}
}
if (nodeLayer.getFunction() == Layer.Function.DIFFP) {
// make sure p-diffusion is in p-select
Poly [] polys = tech.getShapeOfNode(ni);
for (Poly poly : polys) {
if (merge.contains(nSelectLayer, poly)) {
// switch it n-active
newType = nDiffNode;
break;
}
}
}
if (newType == null) continue;
NodeInst newNi = NodeInst.newInstance(newType, ep, ni.getAnchorCenter(), ni.getXSize(), ni.getYSize(), cell);
if (ni.getTrace() != null && ni.getTrace().length > 0)
{
EPoint [] origPoints = ni.getTrace();
Point2D [] points = new Point2D[origPoints.length];
// for some reason getTrace returns points relative to center, but setTrace expects absolute coordinates
for (int i=0; i aIt = tech.getArcs(); aIt.hasNext(); )
{
ArcProto ap = aIt.next();
if (ap.getFunction() == neededFunction) return ap;
}
return null;
*/
Layer.Function fun = layer.getFunction();
if (fun.isPoly() || fun.isMetal()) return arcsForLayer.get(layer);
if (!fun.isDiff()) return null;
ArrayList requiredLayers = new ArrayList();
ArrayList requiredAbsentLayers = new ArrayList();
// must further differentiate the active arcs...find implants
Layer wellP = null, wellN = null;
for(Layer l : originalMerge.getKeySet())
{
if (l.getFunction() == Layer.Function.WELLP) wellP = l;
if (l.getFunction() == Layer.Function.WELLN) wellN = l;
}
// Active must have P-Select or N-Select
if (pSelectLayer != null && originalMerge.intersects(pSelectLayer, poly)) {
if (unifyActive)
requiredLayers.add(activeLayer);
else
requiredLayers.add(realPActiveLayer);
requiredLayers.add(pSelectLayer);
}
if (nSelectLayer != null && originalMerge.intersects(nSelectLayer, poly)) {
if (unifyActive)
requiredLayers.add(activeLayer);
else
requiredLayers.add(realNActiveLayer);
requiredLayers.add(nSelectLayer);
}
// Active could either be an Active arc or a Well arc, depending on well type
if (wellN == null || !originalMerge.intersects(wellN, poly))
requiredAbsentLayers.add(wellN);
if (wellN != null && originalMerge.intersects(wellN, poly))
requiredLayers.add(wellN);
// Active could either be an Active arc or a Well arc, depending on well type
if (wellP == null || !originalMerge.intersects(wellP, poly))
requiredAbsentLayers.add(wellP);
if (wellP != null && originalMerge.intersects(wellP, poly))
requiredLayers.add(wellP);
// now find the arc with the desired function
for(Iterator aIt = tech.getArcs(); aIt.hasNext(); )
{
ArcProto ap = aIt.next();
List apLayers = new ArrayList();
for (Iterator layit = ap.getLayerIterator(); layit.hasNext(); )
apLayers.add(layit.next());
// make sure required layers exist
boolean failed = false;
for (Layer l : requiredLayers) {
if (!apLayers.contains(l)) { failed = true; break; }
}
if (failed) continue;
for (Layer l : requiredAbsentLayers) {
if (apLayers.contains(l)) { failed = true; break; }
}
if (failed) continue;
return ap;
}
return null;
}
/**
* Method to locate a port on a node at a specific point with a specific connectivity.
* @param pt the center location of the desired node.
* @param ap the type of the arc that must connect.
* @param size the size of the node (if it must be created).
* @param newCell the cell in which to locate or place the node.
* @return the port on the node that is at the proper point.
* If there is none there, a node is created.
*/
private PortInst wantConnectingNodeAt(Point2D pt, ArcProto ap, double size, Cell newCell)
{
Rectangle2D bounds = new Rectangle2D.Double(pt.getX(), pt.getY(), 0, 0);
for(Iterator it = newCell.searchIterator(bounds); it.hasNext(); )
{
Geometric geom = it.next();
if (!(geom instanceof NodeInst)) continue;
NodeInst ni = (NodeInst)geom;
for(Iterator pIt = ni.getPortInsts(); pIt.hasNext(); )
{
PortInst pi = pIt.next();
PortProto pp = pi.getPortProto();
if (!pp.connectsTo(ap)) continue;
Poly poly = pi.getPoly();
if (poly.contains(pt)) return pi;
}
}
NodeInst ni = createNode(ap.findPinProto(), pt, size, size, null, newCell);
return ni.getOnlyPortInst();
}
/**
* Method to locate a node at a specific point with a specific type.
* @param pt the center location of the desired node.
* @param pin the type of the desired node.
* @param size the size of the node (if it must be created).
* @param newCell the cell in which to locate or place the node.
* @return a node of that type at that location.
* If there is none there, it is created.
*/
private NodeInst wantNodeAt(Point2D pt, NodeProto pin, double size, Cell newCell)
{
Rectangle2D bounds = new Rectangle2D.Double(pt.getX(), pt.getY(), 0, 0);
for(Iterator it = newCell.searchIterator(bounds); it.hasNext(); )
{
Geometric geom = it.next();
if (!(geom instanceof NodeInst)) continue;
NodeInst ni = (NodeInst)geom;
if (ni.getProto() != pin) continue;
if (ni.getAnchorCenter().equals(pt)) return ni;
}
NodeInst ni = createNode(pin, pt, size, size, null, newCell);
return ni;
}
/**
* Method to find the PortInst on a NodeInst that connects to a given PortProto and is closest to a given point.
* Because some primitive nodes (transistors) may have multiple ports that connect to each other
* (the poly ports) and because the system returns only one of those ports when describing the topology of
* a piece of geometry, it is necessary to find the closest port.
* @param ni the primitive NodeInst being examined.
* @param pp the primitive port on that node which defines the connection to the node.
* @param pt a point close to the desired port.
* @return the PortInst on the node that is electrically connected to the given primitive port, and closest to the point.
*/
private PortInst findPortInstClosestToPoly(NodeInst ni, PrimitivePort pp, Point2D pt)
{
PortInst touchingPi = ni.findPortInstFromProto(pp);
PrimitiveNode pnp = (PrimitiveNode)ni.getProto();
Poly touchingPoly = touchingPi.getPoly();
double bestDist = pt.distance(new Point2D.Double(touchingPoly.getCenterX(), touchingPoly.getCenterY()));
for(Iterator pIt = pnp.getPorts(); pIt.hasNext(); )
{
PrimitivePort prP = (PrimitivePort)pIt.next();
if (prP.getTopology() == pp.getTopology())
{
PortInst testPi = ni.findPortInstFromProto(prP);
Poly testPoly = testPi.getPoly();
double dist = pt.distance(new Point2D.Double(testPoly.getCenterX(), testPoly.getCenterY()));
if (dist < bestDist)
{
bestDist = dist;
touchingPi = testPi;
}
}
}
return touchingPi;
}
private static class Centerline
{
Point2D start, end;
EPoint startUnscaled, endUnscaled;
boolean startHub, endHub;
double width;
boolean handled;
int angle;
Centerline(double width, Point2D start, Point2D end)
{
this.width = width;
this.start = start;
this.startUnscaled = new EPoint(start.getX() / SCALEFACTOR, start.getY() / SCALEFACTOR);
this.endUnscaled = new EPoint(end.getX() / SCALEFACTOR, end.getY() / SCALEFACTOR);
this.end = end;
startHub = endHub = false;
if (start.equals(end)) angle = -1; else
angle = GenMath.figureAngle(end, start);
}
void setStart(double x, double y)
{
start.setLocation(x, y);
startUnscaled = new EPoint(x / SCALEFACTOR, y / SCALEFACTOR);
}
void setEnd(double x, double y)
{
end.setLocation(x, y);
endUnscaled = new EPoint(x / SCALEFACTOR, y / SCALEFACTOR);
}
Rectangle2D getBounds() {
if (start.getX() == end.getX()) {
// vertical
double minX = (start.getX() < end.getX() ? start.getX() : end.getX()) - width/2.0;
double minY = start.getY() < end.getY() ? start.getY() : end.getY();
double maxY = start.getY() > end.getY() ? start.getY() : end.getY();
return new Rectangle2D.Double(minX, minY, width, maxY-minY);
}
if (start.getY() == end.getY()) {
// horizontal
double minY = (start.getY() < end.getY() ? start.getY() : end.getY()) - width/2.0;
double minX = start.getX() < end.getX() ? start.getX() : end.getX();
double maxX = start.getX() > end.getX() ? start.getX() : end.getX();
return new Rectangle2D.Double(minX, minY, maxX-minX, width);
}
return null; // non-Manhattan
}
public String toString()
{
return "CENTERLINE from (" + TextUtils.formatDouble(start.getX()/SCALEFACTOR) + "," +
TextUtils.formatDouble(start.getY()/SCALEFACTOR) + ") to (" +
TextUtils.formatDouble(end.getX()/SCALEFACTOR) + "," +
TextUtils.formatDouble(end.getY()/SCALEFACTOR) + ") wid=" +
TextUtils.formatDouble(width/SCALEFACTOR) + ", len=" +
TextUtils.formatDouble((start.distance(end)/SCALEFACTOR));
}
}
/**
* Method to return the port and location to use for one end of a Centerline.
* @param cl the Centerline to connect
* @param loc1 it's location (values returned through this object!)
* @param layer the layer associated with the Centerline.
* @param ap the type of arc to create.
* @param startSide true to consider the "start" end of the Centerline, false for the "end" end.
* @param newCell the Cell in which to find ports.
* @return the PortInst on the Centerline.
*/
private PortInst locatePortOnCenterline(Centerline cl, Point2D loc1, Layer layer,
ArcProto ap, boolean startSide, Cell newCell)
{
PortInst piRet = null;
boolean isHub = cl.endHub;
gridAlignCenterline(cl, startSide);
EPoint startPoint = cl.endUnscaled;
if (startSide)
{
isHub = cl.startHub;
startPoint = cl.startUnscaled;
}
if (!isHub)
{
List possiblePorts = findPortInstsTouchingPoint(startPoint, layer, newCell, ap);
for(PortInst pi : possiblePorts)
{
Poly portPoly = pi.getPoly();
Point2D [] points = portPoly.getPoints();
if (points.length == 1)
{
Point2D iPt = GenMath.intersect(cl.startUnscaled, cl.angle, points[0], (cl.angle+900)%3600);
if (iPt != null)
{
loc1.setLocation(iPt.getX(), iPt.getY());
piRet = pi;
break;
}
} else
{
if (portPoly.contains(startPoint))
{
loc1.setLocation(startPoint);
piRet = pi;
break;
}
for(int i=0; i Math.max(portLineFrom.getX(), portLineTo.getX()) ||
interPt.getY() < Math.min(portLineFrom.getY(), portLineTo.getY()) ||
interPt.getY() > Math.max(portLineFrom.getY(), portLineTo.getY())) interPt = null;
}
}
if (interPt == null) continue;
loc1.setLocation(interPt.getX(), interPt.getY());
if (!portPoly.contains(loc1)) continue;
piRet = pi;
break;
}
if (piRet != null) break;
}
}
}
if (piRet == null)
{
// shrink the end inward by half-width
PrimitiveNode pin = ap.findPinProto();
int ang = GenMath.figureAngle(cl.start, cl.end);
double xOff = GenMath.cos(ang) * cl.width/2;
double yOff = GenMath.sin(ang) * cl.width/2;
// double aliX = 1, aliY = 1;
// if (alignment != null)
// {
// if (alignment.getWidth() > 0) aliX = scaleUp(alignment.getWidth());
// if (alignment.getHeight() > 0) aliY = scaleUp(alignment.getHeight());
// }
if (startSide)
{
if (!isHub && cl.start.distance(cl.end) > cl.width)
{
//xOff = Math.floor(xOff / aliX) * aliX;
//yOff = Math.floor(yOff / aliY) * aliY;
// if shortening to allow ends extend will put the arc off-grid, do not do it
if (xOff > 0 && (xOff % scaleUp(alignment.getWidth())) != 0) xOff = 0;
if (yOff > 0 && (yOff % scaleUp(alignment.getHeight())) != 0) yOff = 0;
cl.setStart(cl.start.getX() + xOff, cl.start.getY() + yOff);
}
double size = pin.getFactoryDefaultBaseDimension().getLambdaWidth();
NodeInst ni = wantNodeAt(cl.startUnscaled, pin, size, newCell);
loc1.setLocation(cl.startUnscaled.getX(), cl.startUnscaled.getY());
piRet = ni.getOnlyPortInst();
} else
{
if (!isHub && cl.start.distance(cl.end) > cl.width)
{
//xOff = Math.ceil(xOff / aliX) * aliX;
//yOff = Math.ceil(yOff / aliY) * aliY;
// if shortening to allow ends extend will put the arc off-grid, do not do it
if (xOff > 0 && (xOff % scaleUp(alignment.getWidth())) != 0) xOff = 0;
if (yOff > 0 && (yOff % scaleUp(alignment.getHeight())) != 0) yOff = 0;
cl.setEnd(cl.end.getX() - xOff, cl.end.getY() - yOff);
}
NodeInst ni = wantNodeAt(cl.endUnscaled, pin, cl.width / SCALEFACTOR, newCell);
loc1.setLocation(cl.endUnscaled.getX(), cl.endUnscaled.getY());
piRet = ni.getOnlyPortInst();
}
}
return piRet;
}
private void gridAlignCenterline(Centerline cl, boolean startSide)
{
// // grid align the edges
// double halfWidth = cl.width / 2;
//halfWidth = 0; // is this right?
// if (cl.start.getX() == cl.end.getX())
// {
// // vertical arc: make sure ends align in Y
// int ali = (int)Math.round(alignment.getHeight() * SCALEFACTOR);
// if (ali == 0) return;
// if (startSide)
// {
// // adjust the "start" end
// if (cl.start.getY() < cl.end.getY())
// {
// // start on left: compute edge below it
// double edge = cl.start.getY() - halfWidth;
// cl.setStart(cl.start.getX(), Math.ceil(edge / ali) * ali + halfWidth);
// } else
// {
// // start on right: compute edge above it
// double edge = cl.start.getY() + halfWidth;
// cl.setStart(cl.start.getX(), Math.floor(edge / ali) * ali - halfWidth);
// }
// } else
// {
// // adjust the "end" end
// if (cl.end.getY() < cl.start.getY())
// {
// // end on left: compute edge below it
// double edge = cl.end.getY() - halfWidth;
// cl.setEnd(cl.end.getX(), Math.ceil(edge / ali) * ali + halfWidth);
// } else
// {
// // end on right: compute edge above it
// double edge = cl.end.getY() + halfWidth;
// cl.setEnd(cl.end.getX(), Math.floor(edge / ali) * ali - halfWidth);
// }
// }
// } else if (cl.start.getY() == cl.end.getY())
// {
// // horizontal arc: make sure ends align in X
// int ali = (int)Math.round(alignment.getWidth() * SCALEFACTOR);
// if (ali == 0) return;
// if (startSide)
// {
// // adjust the "start" end
// if (cl.start.getX() < cl.end.getX())
// {
// // start on left: compute edge below it
// double edge = cl.start.getX() - halfWidth;
// cl.setStart(Math.ceil(edge / ali) * ali + halfWidth, cl.start.getY());
// } else
// {
// // start on right: compute edge above it
// double edge = cl.start.getX() + halfWidth;
// cl.setStart(Math.floor(edge / ali) * ali - halfWidth, cl.start.getY());
// }
// } else
// {
// // adjust the "end" end
// if (cl.end.getX() < cl.start.getX())
// {
// // end on left: compute edge below it
// double edge = cl.end.getX() - halfWidth;
// cl.setEnd(Math.ceil(edge / ali) * ali + halfWidth, cl.end.getY());
// } else
// {
// // end on right: compute edge above it
// double edge = cl.end.getX() + halfWidth;
// cl.setEnd(Math.floor(edge / ali) * ali - halfWidth, cl.end.getY());
// }
// }
// }
}
private List findPortInstsTouchingPoint(Point2D pt, Layer layer, Cell newCell, ArcProto ap)
{
List touchingNodes = new ArrayList();
boolean mightCreateExports = false;
Rectangle2D checkBounds = new Rectangle2D.Double(pt.getX(), pt.getY(), 0, 0);
for(Iterator it = newCell.searchIterator(checkBounds); it.hasNext(); )
{
Geometric geom = it.next();
if (!(geom instanceof NodeInst)) continue;
NodeInst ni = (NodeInst)geom;
if (ni.isCellInstance())
{
boolean found = false;
for(Iterator pIt = ni.getPortInsts(); pIt.hasNext(); )
{
PortInst pi = pIt.next();
Poly portPoly = pi.getPoly();
if (portPoly.contains(pt))
{
touchingNodes.add(pi);
found = true;
break;
}
}
if (found) continue;
// remember that a cell was found...might have to create exports on it
mightCreateExports = true;
continue;
}
// for pins, must be centered over the desired point
if (ni.getFunction().isPin())
{
if (!ni.getOnlyPortInst().getPortProto().connectsTo(ap)) continue;
if (ni.getAnchorCenter().equals(pt))
{
touchingNodes.add(ni.getOnlyPortInst());
}
} else
{
// non-pins can have any touching and connecting layer
Poly [] polys = tech.getShapeOfNode(ni, true, true, null);
FixpTransform trans = ni.rotateOut();
for(int i=0; i it = cell.searchIterator(checkBounds); it.hasNext();)
{
Geometric geom = it.next();
if (!(geom instanceof NodeInst)) continue;
NodeInst subNi = (NodeInst)geom;
if (subNi.isCellInstance()) continue;
Technology tech = subNi.getProto().getTechnology();
FixpTransform trans = subNi.rotateOut();
Poly [] polyList = tech.getShapeOfNode(subNi, true, true, null);
for(int i=0; i it = cell.searchIterator(checkBounds); it.hasNext(); )
{
Geometric geom = it.next();
if (!(geom instanceof NodeInst)) continue;
NodeInst subNi = (NodeInst)geom;
PortInst foundPi = null;
if (subNi.isCellInstance())
{
FixpTransform transIn = subNi.rotateIn(subNi.translateIn());
Point2D inside = new Point2D.Double();
transIn.transform(pt, inside);
Cell subCell = (Cell)subNi.getProto();
PortInst pi = makePort(subCell, layer, inside);
if (pi != null)
{
// already exported?
for(Iterator eIt = pi.getNodeInst().getExports(); eIt.hasNext(); )
{
Export e = eIt.next();
if (e.getOriginalPort() == pi)
{
foundPi = subNi.findPortInstFromProto(e);
return foundPi;
}
}
// if not already exported, make the export now
if (foundPi == null)
{
Netlist nl = subCell.getNetlist();
Network net = nl.getNetwork(pi);
String exportName = null;
for(Iterator nIt = net.getExportedNames(); nIt.hasNext(); )
{
String eName = nIt.next();
if (eName.startsWith("E"))
{
boolean isFake = false;
for(Export e : generatedExports)
{
if (e.getParent() == subCell && e.getName().equals(eName)) { isFake = true; break; }
}
if (isFake) continue;
}
if (exportName == null || exportName.length() < eName.length()) exportName = eName;
}
boolean genFakeName = (exportName == null);
if (genFakeName) exportName = "E";
exportName = ElectricObject.uniqueObjectName(exportName, subCell, Export.class, true, true);
Export e = Export.newInstance(subCell, pi, exportName, ep);
if (genFakeName)
generatedExports.add(e);
foundPi = subNi.findPortInstFromProto(e);
return foundPi;
}
}
}
}
return null;
}
/********************************************** VIA/CONTACT EXTRACTION **********************************************/
private static class PossibleVia
{
PrimitiveNode pNp;
int rotation;
double minWidth, minHeight;
double multicutSep1D, multicutSep2D, multicutSizeX, multicutSizeY;
Layer [] layers;
double [] shrinkL, shrinkR, shrinkT, shrinkB;
PossibleVia(PrimitiveNode pNp, int numLayers)
{
this.pNp = pNp;
rotation = 0;
layers = new Layer[numLayers];
shrinkL = new double[numLayers];
shrinkR = new double[numLayers];
shrinkT = new double[numLayers];
shrinkB = new double[numLayers];
}
}
/**
* Method to scan the geometric information for possible contacts and vias.
* Any vias found are created in the new cell and removed from the geometric information.
* @param merge the current geometry being extracted.
* @param originalMerge the original geometry.
* @param newCell the Cell where new geometry is being created.
* @return false if the job was aborted.
*/
private boolean extractVias(PolyMerge merge, PolyMerge originalMerge, Cell oldCell, Cell newCell, boolean usePureLayerNodes)
{
// make a list of all via/cut layers in the technology and count the number of vias/cuts
int totalCuts = 0;
List layers = new ArrayList();
for (Layer layer : allCutLayers.keySet())
{
layers.add(layer);
CutInfo cInfo = allCutLayers.get(layer);
totalCuts += cInfo.getNumCuts();
}
// initialize list of nodes that have been created
List contactNodes = new ArrayList();
// examine all vias/cuts for possible contacts
int soFar = 0;
for (Layer layer : layers)
{
// compute the possible via nodes that this layer could become
List possibleVias = findPossibleVias(layer);
// make a list of all necessary layers
Set layersToExamine = new TreeSet();
for(PossibleVia pv : possibleVias)
{
for(int i=0; i cutsNotExtracted = new ArrayList();
while (cInfo.getNumCuts() > 0)
{
PolyBase cut = cInfo.getFirstCut();
soFar++;
if (!recursive && (soFar % 100) == 0) Job.getUserInterface().setProgressValue(soFar * 100 / totalCuts);
// figure out which of the layers is present at this cut point
Rectangle2D cutBox = cut.getBox();
if (cutBox == null)
{
cutBox = cut.getBounds2D();
double centerX = cutBox.getCenterX()/SCALEFACTOR;
double centerY = cutBox.getCenterY()/SCALEFACTOR;
String msg = "Cannot extract nonManhattan contact cut at (" + TextUtils.formatDistance(centerX) +
"," + TextUtils.formatDistance(centerY) + ")";
addErrorLog(newCell, msg, new EPoint(centerX, centerY));
cInfo.removeCut(cut);
cutsNotExtracted.add(cut);
continue;
}
Point2D ctr = new Point2D.Double(cutBox.getCenterX(), cutBox.getCenterY());
Set layersPresent = new TreeSet();
for(Layer l : layersToExamine)
{
boolean layerAtPoint = originalMerge.contains(l, ctr);
if (layerAtPoint) layersPresent.add(geometricLayer(l));
}
boolean ignorePWell = false, ignoreNWell = false;
if (pSubstrateProcess)
{
// P-substrate process (P-well is presumed where there is no N-Well)
boolean foundNWell = false;
for(Layer l : layersPresent)
{
if (l.getFunction() == Layer.Function.WELLN) { foundNWell = true; break; }
}
if (!foundNWell) ignorePWell = true;
}
if (nSubstrateProcess)
{
// N-Substrate process (N-well is presumed where there is no P-Well)
boolean foundPWell = false;
for(Layer l : layersPresent)
{
if (l.getFunction() == Layer.Function.WELLP) { foundPWell = true; break; }
}
if (!foundPWell) ignoreNWell = true;
}
boolean foundCut = false;
String reason = null;
for(PossibleVia pv : possibleVias)
{
// quick test to see if this via could possibly exist
List missingLayers = null;
for(int i=0; i();
missingLayers.add(pv.layers[i]);
break;
}
}
if (missingLayers != null)
{
reason = "layers are missing:";
for(Layer l : missingLayers) reason += " " + l.getName();
continue;
}
if (DEBUGCONTACTS) System.out.println("CONSIDERING "+pv.pNp.describe(false)+" ROTATED "+pv.rotation+" AT ("+
TextUtils.formatDouble(cutBox.getCenterX()/SCALEFACTOR)+","+
TextUtils.formatDouble(cutBox.getCenterY()/SCALEFACTOR)+")...");
// see if this is an active/poly layer (in which case, there can be no poly/active in the area)
boolean activeCut = false;
boolean polyCut = false;
NodeLayer [] primLayers = pv.pNp.getNodeLayers();
for(int i=0; i cutsInArea = new HashSet();
cutsInArea.add(cut);
Rectangle2D multiCutBounds = (Rectangle2D)cutBox.clone();
double cutLimit = Math.ceil(Math.max(pv.multicutSep1D, pv.multicutSep2D) +
Math.max(pv.multicutSizeX, pv.multicutSizeY)) * SCALEFACTOR;
boolean foundMore = true;
double xspacing = 0, yspacing = 0;
while (foundMore)
{
foundMore = false;
Rectangle2D searchArea = new Rectangle2D.Double(multiCutBounds.getMinX()-cutLimit, multiCutBounds.getMinY()-cutLimit,
multiCutBounds.getWidth() + cutLimit*2, multiCutBounds.getHeight() + cutLimit*2);
/*
Rectangle2D searchArea2 = new Rectangle2D.Double(searchArea.getMinX()/SCALEFACTOR, searchArea.getMinY()/SCALEFACTOR,
searchArea.getWidth()/SCALEFACTOR, searchArea.getHeight()/SCALEFACTOR);
System.out.println("Checking search area "+searchArea2);
*/
for(Iterator sea = new RTNode.Search(searchArea, cInfo.getRTree(), true); sea.hasNext(); )
{
CutBound cBound = sea.next();
if (cutsInArea.contains(cBound.cut)) continue;
Rectangle2D bound = cBound.getBounds();
if (!searchArea.contains(bound.getCenterX(), bound.getCenterY())) continue;
// use only cuts at a spacing that matches (a multiple of) the nearest contact spacing
double distX = cut.getCenterX() - bound.getCenterX();
double distY = cut.getCenterY() - bound.getCenterY();
if (xspacing == 0) xspacing = distX;
else if (distX % xspacing != 0) continue;
if (yspacing == 0) yspacing = distY;
else if (distY % yspacing != 0) continue;
/*
// make sure cuts are in a contiguous array at the initial spacing
if (furthestX == 0) furthestX = distX;
if (furthestY == 0) furthestY = distY;
if (distX > furthestX) {
if (distX == furthestX + xspacing)
furthestX = distX; // this is the next one on grid
else
continue; // on grid, but not contiguous
}
if (distY > furthestY) {
if (distY == furthestY + yspacing)
furthestY = distY; // this is the next one on grid
else
continue; // on grid, but not contiguous
}
// record height of first column
if (maxColumnHeight == 0 && distX != 0) {
// first contact of the second column
maxColumnHeight = furthestY;
}
*/
double lX = Math.min(multiCutBounds.getMinX(), bound.getMinX());
double hX = Math.max(multiCutBounds.getMaxX(), bound.getMaxX());
double lY = Math.min(multiCutBounds.getMinY(), bound.getMinY());
double hY = Math.max(multiCutBounds.getMaxY(), bound.getMaxY());
Rectangle2D newMultiCutBounds = new Rectangle2D.Double(lX, lY, hX-lX, hY-lY);
// make sure the expanded area has both metal layers in it
boolean fits = true;
PolyBase layerPoly = new PolyBase(newMultiCutBounds);
for(int i=0; i rectVias = getLargestRectangleOfVias(cutsInArea, cut, xspacing, yspacing, multiCutBounds);
cutsInArea.clear();
cutsInArea.addAll(rectVias);
multiCutBounds = (Rectangle2D)cutBox.clone();
for (PolyBase via : rectVias) {
Rectangle2D bound = via.getBounds2D();
double lX = Math.min(multiCutBounds.getMinX(), bound.getMinX());
double hX = Math.max(multiCutBounds.getMaxX(), bound.getMaxX());
double lY = Math.min(multiCutBounds.getMinY(), bound.getMinY());
double hY = Math.max(multiCutBounds.getMaxY(), bound.getMaxY());
multiCutBounds = new Rectangle2D.Double(lX, lY, hX-lX, hY-lY);
}
if (DEBUGCONTACTS) System.out.println(" FOUND LARGE CONTACT WITH "+cutsInArea.size()+" CUTS, IN "+
TextUtils.formatDouble(multiCutBounds.getMinX()/SCALEFACTOR)+"<=X<="+TextUtils.formatDouble(multiCutBounds.getMaxX()/SCALEFACTOR)+" AND "+
TextUtils.formatDouble(multiCutBounds.getMinY()/SCALEFACTOR)+"<=Y<="+TextUtils.formatDouble(multiCutBounds.getMaxY()/SCALEFACTOR));
// determine size of possible multi-cut contact
double trueWidth = pv.minWidth;
double trueHeight = pv.minHeight;
if (pv.rotation == 90 || pv.rotation == 270)
{
trueWidth = pv.minHeight;
trueHeight = pv.minWidth;
}
double lX = cutBox.getCenterX(), hX = cutBox.getCenterX();
double lY = cutBox.getCenterY(), hY = cutBox.getCenterY();
for(PolyBase cutBound : cutsInArea)
{
if (cutBound.getCenterX() < lX) lX = cutBound.getCenterX();
if (cutBound.getCenterX() > hX) hX = cutBound.getCenterX();
if (cutBound.getCenterY() < lY) lY = cutBound.getCenterY();
if (cutBound.getCenterY() > hY) hY = cutBound.getCenterY();
}
lX -= trueWidth/2; hX += trueWidth/2;
lY -= trueHeight/2; hY += trueHeight/2;
Rectangle2D contactBound = new Rectangle2D.Double(lX, lY, hX-lX, hY-lY);
// see if largest multi-cut contact fits
Layer badLayer = doesNodeFit(pv, multiCutBounds, originalMerge, ignorePWell, ignoreNWell);
if (badLayer == null)
{
// it fits: see if a the cuts fit
double mw = contactBound.getWidth();
double mh = contactBound.getHeight();
if (pv.rotation == 90 || pv.rotation == 270)
{
mw = contactBound.getHeight();
mh = contactBound.getWidth();
}
if (DEBUGCONTACTS) System.out.println(" METAL LAYERS OF LARGE CONTACT FITS...NOW CHECKING CUTS ON "+
TextUtils.formatDouble(mw/SCALEFACTOR)+"x"+TextUtils.formatDouble(mh/SCALEFACTOR)+" NODE");
badLayer = realizeBiggestContact(pv.pNp, Technology.NodeLayer.MULTICUT_CENTERED,
contactBound.getCenterX(), contactBound.getCenterY(), mw, mh,
pv.rotation*10, originalMerge, newCell, contactNodes, activeCut, polyCut, usePureLayerNodes, cutsInArea);
if (badLayer == null)
{
for(PolyBase cutFound : cutsInArea)
cInfo.removeCut(cutFound);
soFar += cutsInArea.size() - 1;
foundCut = true;
break;
}
if (cutsInArea.size() > 1 && pv.pNp.findMulticut() != null)
{
Layer spreadBadLayer = realizeBiggestContact(pv.pNp, Technology.NodeLayer.MULTICUT_SPREAD,
contactBound.getCenterX(), contactBound.getCenterY(), mw, mh,
pv.rotation*10, originalMerge, newCell, contactNodes, activeCut, polyCut, usePureLayerNodes, cutsInArea);
if (spreadBadLayer == null)
{
for(PolyBase cutFound : cutsInArea)
cInfo.removeCut(cutFound);
soFar += cutsInArea.size() - 1;
foundCut = true;
break;
}
spreadBadLayer = realizeBiggestContact(pv.pNp, Technology.NodeLayer.MULTICUT_CORNER,
contactBound.getCenterX(), contactBound.getCenterY(), mw, mh,
pv.rotation*10, originalMerge, newCell, contactNodes, activeCut, polyCut, usePureLayerNodes, cutsInArea);
if (spreadBadLayer == null)
{
for(PolyBase cutFound : cutsInArea)
cInfo.removeCut(cutFound);
soFar += cutsInArea.size() - 1;
foundCut = true;
break;
}
}
if (DEBUGCONTACTS) System.out.println(" LARGE CONTACT CUTS DO NOT FIT (LAYER "+badLayer.getName()+")");
}
// try for exact cut placement with a single contact
if (DEBUGCONTACTS) System.out.println(" CONSIDER SMALL CONTACT IN "+
TextUtils.formatDouble(cutBox.getMinX()/SCALEFACTOR)+"<=X<="+
TextUtils.formatDouble(cutBox.getMaxX()/SCALEFACTOR)+" AND "+
TextUtils.formatDouble(cutBox.getMinY()/SCALEFACTOR)+"<=Y<="+
TextUtils.formatDouble(cutBox.getMaxY()/SCALEFACTOR));
badLayer = doesNodeFit(pv, cutBox, originalMerge, ignorePWell, ignoreNWell);
if (badLayer == null)
{
// it fits: create it
realizeNode(pv.pNp, Technology.NodeLayer.MULTICUT_CENTERED, cutBox.getCenterX(), cutBox.getCenterY(),
pv.minWidth, pv.minHeight, pv.rotation*10, null, originalMerge, newCell, contactNodes, usePureLayerNodes);
cInfo.removeCut(cut);
foundCut = true;
break;
}
reason = "node " + pv.pNp.describe(false) + ", layer " + badLayer.getName() + " does not fit";
if (pv.rotation != 0) reason += " (when rotated " + pv.rotation + ")";
}
if (!foundCut)
{
double centerX = cutBox.getCenterX()/SCALEFACTOR;
double centerY = cutBox.getCenterY()/SCALEFACTOR;
String msg = "Cell " + newCell.describe(false) + ": Did not extract contact " +
cut.getLayer().getName() + " cut at (" + TextUtils.formatDouble(centerX) + "," +
TextUtils.formatDouble(centerY) + ")";
if (reason != null) msg += " because " + reason;
addErrorLog(newCell, msg, new EPoint(centerX, centerY));
cInfo.removeCut(cut);
cutsNotExtracted.add(cut);
}
}
for(PolyBase pb : cutsNotExtracted)
cInfo.addCut(pb);
}
// now remove all created contacts from the original merge
if (!startSection(oldCell, "Finished extracting " + contactNodes.size() + " vias..."))
return false; // aborted
// build an R-Tree of all created nodes
RTNode root = RTNode.makeTopLevel();
for(NodeInst ni : contactNodes)
root = RTNode.linkGeom(null, root, ni);
// recursively scan the R-Tree, merging geometry on created nodes and removing them from the main merge
if (!usePureLayerNodes)
{
PolyMerge subtractMerge = new PolyMerge();
extractContactNodes(root, merge, subtractMerge, 0, contactNodes.size());
merge.subtractMerge(subtractMerge);
}
return true;
}
/**
* Multi-cut vias must be rectangular - get the largest rectangle that fits in the set of vias,
* that includes the initial via
* @param vias set of vias
* @param initialVia the initial via
* @param xspacing x spacing between vias
* @param yspacing y spacing between vias
* @param bounds bounds of all the vias
* @return the subset of vias that form a contiguous rectangle, containing the initial via
*/
private Set getLargestRectangleOfVias(Set vias, PolyBase initialVia, double xspacing, double yspacing, Rectangle2D bounds)
{
xspacing = Math.abs(xspacing);
yspacing = Math.abs(yspacing);
if (xspacing == 0) xspacing = SCALEFACTOR;
if (yspacing == 0) yspacing = SCALEFACTOR;
int numViasWide = (int)Math.ceil(bounds.getWidth() / xspacing);
int numViasHigh = (int)Math.ceil(bounds.getHeight() / yspacing);
PolyBase [][] viaMap = new PolyBase[numViasWide][numViasHigh];
int nomX = 0, nomY = 0;
for (int x=0; x=0; y--) {
if (viaMap[x][y] == null) break;
colMinY = y;
}
if (initial) minY = colMinY; // initial column
else if (colMinY > minY) minY = colMinY;
initial = false;
}
// find maxX and minX from initial via
int maxX = nomX, minX = nomX;
initial = true;
for (int y=0; y=0; x--) {
if (viaMap[x][y] == null) break;
colMinX = x;
}
if (initial) minX = colMinX; // initial row
else if (colMinX > minX) minX = colMinX;
initial = false;
}
// get rectangle
Set rectVias = new HashSet();
for (int x=minX; x<=maxX; x++) {
for (int y=minY; y<=maxY; y++) {
rectVias.add(viaMap[x][y]);
}
}
return rectVias;
}
/**
* Method to create the biggest contact node in a given location.
* @param pNp the type of node to create.
* @param cutVariation the contact cut spacing rule.
* @param x the center X coordinate of the node.
* @param y the center Y coordinate of the node.
* @param sX the initial width of the node.
* @param sY the initial height of the node.
* @param rot the rotation of the node.
* @param merge the merge in which this node must reside.
* @param newCell the Cell in which the node will be created.
* @param contactNodes a list of nodes that were created.
* @param activeCut true if this is an active cut and must not have poly in the area.
* @param polyCut true if this is an poly cut and must not have active in the area.
* @param cutsInArea the cut polygons in the area (for exact matching).
* @return null if successful, otherwise the polygon that could not be matched.
*/
private Layer realizeBiggestContact(PrimitiveNode pNp, int cutVariation, double x, double y, double sX, double sY, int rot,
PolyMerge merge, Cell newCell, List contactNodes, boolean activeCut, boolean polyCut, boolean usePureLayerNodes,
Set cutsInArea)
{
//boolean debug = pNp.getName().equals("Z-Metal-1-N-Diff-Con");
//if (debug) System.out.println("LOOKING FOR LARGEST CUT...");
Orientation orient = Orientation.fromAngle(rot);
if (alignment != null)
{
if (alignment.getWidth() > 0)
{
double scale = scaleUp(alignment.getWidth());
x = Math.round(x / scale) * scale;
}
if (alignment.getHeight() > 0)
{
double scale = scaleUp(alignment.getHeight());
y = Math.round(y / scale) * scale;
}
}
EPoint ctr = new EPoint(x / SCALEFACTOR, y / SCALEFACTOR);
// first find an X size that does not fit
double lowXInc = 0;
double highXInc = SCALEFACTOR*2;
for(;;)
{
NodeInst ni = makeDummyNodeInst(pNp, ctr, sX+highXInc, sY, orient, cutVariation);
PolyBase error = dummyNodeFits(ni, merge, activeCut, polyCut, cutsInArea);
//if (debug) System.out.println(" GROWING X..."+TextUtils.formatDouble((sX+highXInc)/SCALEFACTOR)+"x"+TextUtils.formatDouble(sY/SCALEFACTOR)+(error==null?" FITS":" DOES NOT FIT ("+error.getLayer().getName()+")"));
if (error != null) break;
lowXInc = highXInc;
highXInc *= 2;
}
// now iterate to find the precise node X size
for(;;)
{
if (highXInc - lowXInc <= 1) { lowXInc = Math.floor(lowXInc); break; }
double medInc = (lowXInc + highXInc) / 2;
NodeInst ni = makeDummyNodeInst(pNp, ctr, sX+medInc, sY, orient, cutVariation);
PolyBase error = dummyNodeFits(ni, merge, activeCut, polyCut, cutsInArea);
//if (debug) System.out.println(" ITERATING X..."+TextUtils.formatDouble((sX+medInc)/SCALEFACTOR)+"x"+TextUtils.formatDouble(sY/SCALEFACTOR)+(error==null?" FITS":" DOES NOT FIT ("+error.getLayer().getName()+")"));
if (error == null) lowXInc = medInc; else
highXInc = medInc;
}
// first find an Y size that does not fit
double lowYInc = 0;
double highYInc = SCALEFACTOR*2;
for(;;)
{
NodeInst ni = makeDummyNodeInst(pNp, ctr, sX, sY+highYInc, orient, cutVariation);
PolyBase error = dummyNodeFits(ni, merge, activeCut, polyCut, cutsInArea);
//if (debug) System.out.println(" GROWING Y..."+TextUtils.formatDouble(sX/SCALEFACTOR)+"x"+TextUtils.formatDouble((sY+highYInc)/SCALEFACTOR)+(error==null?" FITS":" DOES NOT FIT ("+error.getLayer().getName()+")"));
if (error != null) break;
lowYInc = highYInc;
highYInc *= 2;
}
// now iterate to find the precise node Y size
for(;;)
{
if (highYInc - lowYInc <= 1) { lowYInc = Math.floor(lowYInc); break; }
double medInc = (lowYInc + highYInc) / 2;
NodeInst ni = makeDummyNodeInst(pNp, ctr, sX, sY+medInc, orient, cutVariation);
PolyBase error = dummyNodeFits(ni, merge, activeCut, polyCut, cutsInArea);
//if (debug) System.out.println(" ITERATING Y..."+TextUtils.formatDouble(sX/SCALEFACTOR)+"x"+TextUtils.formatDouble((sY+medInc)/SCALEFACTOR)+(error==null?" FITS":" DOES NOT FIT ("+error.getLayer().getName()+")"));
if (error == null) lowYInc = medInc; else
highYInc = medInc;
}
if (!approximateCuts)
{
// make sure the basic node fits
NodeInst ni = makeDummyNodeInst(pNp, ctr, sX+lowXInc, sY+lowYInc, orient, cutVariation);
PolyBase error = dummyNodeFits(ni, merge, activeCut, polyCut, cutsInArea);
//if (debug) System.out.println(" CHECK FINAL..."+TextUtils.formatDouble((sX+lowXInc)/SCALEFACTOR)+"x"+TextUtils.formatDouble((sY+lowYInc)/SCALEFACTOR)+(error==null?" FITS":" DOES NOT FIT ("+error.getLayer().getName()+")"));
if (error != null)
{
// incrementing both axes fails: try one at a time
if (lowXInc > lowYInc)
{
ni = makeDummyNodeInst(pNp, ctr, sX+lowXInc, sY, orient, cutVariation);
error = dummyNodeFits(ni, merge, activeCut, polyCut, cutsInArea);
//if (debug) System.out.println(" FINAL, JUST X..."+TextUtils.formatDouble((sX+lowXInc)/SCALEFACTOR)+"x"+TextUtils.formatDouble((sY)/SCALEFACTOR)+(error==null?" FITS":" DOES NOT FIT ("+error.getLayer().getName()+")"));
if (error == null) sX += lowXInc;
} else
{
ni = makeDummyNodeInst(pNp, ctr, sX, sY+lowYInc, orient, cutVariation);
error = dummyNodeFits(ni, merge, activeCut, polyCut, cutsInArea);
//if (debug) System.out.println(" FINAL, JUST Y..."+TextUtils.formatDouble((sX)/SCALEFACTOR)+"x"+TextUtils.formatDouble((sY+lowYInc)/SCALEFACTOR)+(error==null?" FITS":" DOES NOT FIT ("+error.getLayer().getName()+")"));
if (error == null) sY += lowYInc;
}
} else { sX += lowXInc; sY += lowYInc; }
if (error != null) return error.getLayer();
}
if (alignment != null)
{
if (alignment.getWidth() > 0)
{
double scale = scaleUp(alignment.getWidth()) * 2;
sX = Math.floor(sX / scale) * scale;
}
if (alignment.getHeight() > 0)
{
double scale = scaleUp(alignment.getHeight()) * 2;
sY = Math.floor(sY / scale) * scale;
}
}
realizeNode(pNp, cutVariation, x, y, sX, sY, rot, null, merge, newCell, contactNodes, usePureLayerNodes);
return null;
}
private NodeInst makeDummyNodeInst(PrimitiveNode pNp, EPoint ctr, double sX, double sY, Orientation orient, int cutVariation)
{
NodeInst ni = NodeInst.makeDummyInstance(pNp, ep, ctr, sX / SCALEFACTOR, sY / SCALEFACTOR, orient);
if (ni == null) return null;
if (cutVariation != Technology.NodeLayer.MULTICUT_CENTERED)
ni.newVar(NodeLayer.CUT_ALIGNMENT, Integer.valueOf(cutVariation), ep);
return ni;
}
private PolyBase dummyNodeFits(NodeInst ni, PolyMerge merge, boolean activeCut, boolean polyCut, Set cutsInArea)
{
FixpTransform trans = ni.rotateOut();
Technology tech = ni.getProto().getTechnology();
Poly [] polys = tech.getShapeOfNode(ni);
double biggestArea = 0;
Poly biggestPoly = null;
List cutsFound = null;
if (!approximateCuts) cutsFound = new ArrayList();
boolean hasPplus = false;
boolean hasNplus = false;
boolean hasActive = false;
for(Poly poly : polys)
{
Layer l = poly.getLayer();
if (l == null) continue;
l = geometricLayer(l);
// ignore well layers if the process doesn't have them (in this case they are substrate layers)
if (l.getFunction() == Layer.Function.WELLP && pSubstrateProcess) continue;
if (l.getFunction() == Layer.Function.WELLN && nSubstrateProcess) continue;
if (l.isDiffusionLayer()) hasActive = true;
if (l.getFunction() == Layer.Function.IMPLANTN) hasNplus = true;
if (l.getFunction() == Layer.Function.IMPLANTP) hasPplus = true;
poly.setLayer(l);
poly.transform(trans);
if (l.getFunction().isContact())
{
if (!approximateCuts) cutsFound.add(poly);
continue;
}
double area = poly.getArea();
if (area > biggestArea)
{
biggestArea = area;
biggestPoly = poly;
}
Point2D[] points = poly.getPoints();
for(int i=0; i 0) return cutsFound.get(0);
}
// contact fits, now check for active or poly problems
if (activeCut && biggestPoly != null)
{
// disallow poly in the contact area
if (merge.intersects(polyLayer, biggestPoly)) return biggestPoly;
}
if (polyCut && biggestPoly != null)
{
// disallow active in the contact area
if (pActiveLayer != null && merge.intersects(pActiveLayer, biggestPoly)) return biggestPoly;
if (nActiveLayer != null && nActiveLayer != pActiveLayer && merge.intersects(nActiveLayer, biggestPoly)) return biggestPoly;
}
return null;
}
/**
* Class to define all of the cuts on a given layer.
*/
private static class CutInfo
{
private Map cutMap;
private List justCuts;
private RTNode cutOrg;
public CutInfo()
{
cutMap = new HashMap();
justCuts = new ArrayList();
cutOrg = RTNode.makeTopLevel();
}
public List getCuts() { return justCuts; }
public int getNumCuts() { return justCuts.size(); }
public PolyBase getFirstCut() { return justCuts.get(0); }
public RTNode getRTree() { return cutOrg; }
public void sortCuts()
{
Collections.sort(justCuts, new CutsByLocation());
}
public void addCut(PolyBase cut)
{
CutBound cb = new CutBound(cut);
cutOrg = RTNode.linkGeom(null, cutOrg, cb);
cutMap.put(cut, cb);
justCuts.add(cut);
}
public void removeCut(PolyBase cut)
{
CutBound cb = cutMap.get(cut);
cutOrg = RTNode.unLinkGeom(null, cutOrg, cb);
cutMap.remove(cut);
justCuts.remove(cut);
}
}
/**
* Class to sort CutsByLocation objects by their location.
*/
private static class CutsByLocation implements Comparator
{
public int compare(PolyBase pb1, PolyBase pb2)
{
double pb1x = pb1.getCenterX();
double pb1y = pb1.getCenterY();
double pb2x = pb2.getCenterX();
double pb2y = pb2.getCenterY();
if (pb1x < pb2x) return 1;
if (pb1x > pb2x) return -1;
if (pb1y < pb2y) return 1;
if (pb1y > pb2y) return -1;
return 0;
}
}
/**
* Class to define an R-Tree leaf node for geometry in the via cuts.
*/
private static class CutBound implements RTBounds
{
private PolyBase cut;
CutBound(PolyBase cut)
{
this.cut = cut;
}
@Override
public FixpRectangle getBounds() { return cut.getBounds2D(); }
}
/**
* Method to recursively scan the R-Tree of created contact nodes to remove their geometry from the main merge.
* @param root the current position in the scan of the R-Tree.
* @param merge the main merge.
* @param subtractMerge a merge of created contact nodes that will be subtracted from the main merge.
* @param soFar the number of contacts handled so far.
* @param totalContacts the total number of contacts to handle.
* @return the number of contacts handled so far.
*/
private int extractContactNodes(RTNode root, PolyMerge merge, PolyMerge subtractMerge, int soFar, int totalContacts)
{
for(int j=0; j allLayers = new ArrayList();
for(Layer lay : subtractMerge.getKeySet())
allLayers.add(lay);
for(Layer lay : allLayers)
subtractMerge.deleteLayer(lay);
}
} else
{
RTNode child = root.getChildTree(j);
soFar = extractContactNodes(child, merge, subtractMerge, soFar, totalContacts);
}
}
return soFar;
}
/**
* Method to return a list of PossibleVia objects for a given cut/via layer.
* @param lay the cut/via layer to find.
* @return a List of PossibleVia objects that use the layer as a contact.
*/
private List findPossibleVias(Layer lay)
{
List possibleVias = new ArrayList();
for(Iterator nIt = tech.getNodes(); nIt.hasNext(); )
{
PrimitiveNode pNp = nIt.next();
PrimitiveNode.Function fun = pNp.getFunction();
if (!fun.isContact() && fun != PrimitiveNode.Function.WELL &&
fun != PrimitiveNode.Function.SUBSTRATE) continue;
// TODO do we really need to check each contact?
// For some reason, the MOCMOS technology with MOCMOS foundry shows the "A-" nodes (A-Metal-1-Metal-2-Contact)
// but these primitives are not fully in existence, and crash here
boolean bogus = false;
Technology.NodeLayer [] nLs = pNp.getNodeLayers();
for(int i=0; i pvLayers = new ArrayList();
for(int i=0; i highMetal) { int s = lowMetal; lowMetal = highMetal; highMetal = s; }
if (lowMetal != highMetal-1) badContact = true;
}
if (badContact)
{
if (!bogusContacts.contains(pNp))
{
bogusContacts.add(pNp);
System.out.println("Not extracting unusual via contact: " + pNp.describe(false));
}
continue;
}
}
// create the PossibleVia to describe the geometry
PossibleVia pv = new PossibleVia(pNp, pvLayers.size());
for(int i=0; i
{
private final EditingPreferences ep;
private ViasBySize(EditingPreferences ep) {
this.ep = ep;
}
public int compare(PossibleVia pv1, PossibleVia pv2)
{
double area1 = 0;
Technology.NodeLayer [] layers1 = pv1.pNp.getNodeLayers();
double sizeX = pv1.pNp.getDefSize(ep).getLambdaX();
double sizeY = pv1.pNp.getDefSize(ep).getLambdaY();
for(int i=0; i pTransistors = new ArrayList();
List nTransistors = new ArrayList();
for(Iterator it = tech.getNodes(); it.hasNext(); )
{
PrimitiveNode pNp = it.next();
if (pNp.getFunction().isPTypeTransistor() || pNp.getFunction().isNTypeTransistor())
{
List listToUse = pTransistors;
if (pNp.getFunction().isNTypeTransistor()) listToUse = nTransistors;
boolean same = true;
/*
// JKG: took out this code because it either drops all the "special transistors"
// that have an extra layer (OD18, VTH, VTL), or drops all the regular transistors -
// which one it does is dependent on the order of nodes returned by tech, which is
// apparently not consistent.
// I don't know why this code was needed - optimization?
if (listToUse.size() > 0)
{
// make sure additional transistors have the same overall structure
Set usedLayers = new TreeSet();
Map usageFirst = new HashMap();
Technology.NodeLayer [] layersFirst = listToUse.get(0).getNodeLayers();
for(int i=0; i usageNew = new HashMap();
Technology.NodeLayer [] layersNew = pNp.getNodeLayers();
for(int i=0; i 0)
findTransistors(pTransistors, pActiveLayer, merge, originalMerge, newCell, usePureLayerNodes);
} else
{
if (nTransistors.size() > 0)
findTransistors(nTransistors, nActiveLayer, merge, originalMerge, newCell, usePureLayerNodes);
if (pTransistors.size() > 0)
findTransistors(pTransistors, pActiveLayer, merge, originalMerge, newCell, usePureLayerNodes);
}
}
private void findTransistors(List transistors, Layer activeLayer, PolyMerge merge,
PolyMerge originalMerge, Cell newCell, boolean usePureLayerNodes)
{
originalMerge.intersectLayers(polyLayer, activeLayer, tempLayer1);
List polyList = getMergePolys(originalMerge, tempLayer1, null);
if (polyList != null)
{
for(PolyBase poly : polyList)
{
// look at all of the pieces of this layer
Rectangle2D transBox = poly.getBox();
double cX = poly.getCenterX(), cY = poly.getCenterY();
if (alignment != null)
{
// centers can be off-grid by a factor of 2, because only the edges matter for off-grid
if (alignment.getWidth() > 0)
cX = Math.round(cX / scaleUp(alignment.getWidth()/2)) * scaleUp(alignment.getWidth()/2);
if (alignment.getHeight() > 0)
cY = Math.round(cY / scaleUp(alignment.getHeight()/2)) * scaleUp(alignment.getHeight()/2);
}
if (transBox == null)
{
// complex polygon: extract angled or serpentine transistor
extractNonManhattanTransistor(poly, transistors.get(0), merge, originalMerge, newCell, usePureLayerNodes);
} else
{
EPoint ctr = new EPoint(cX/SCALEFACTOR, cY/SCALEFACTOR);
Map> errorInfo = new HashMap>();
for(PrimitiveNode transistor : transistors)
{
// figure out which way the poly runs in the desired transistor
NodeInst dni = NodeInst.makeDummyInstance(transistor, ep);
Poly [] polys = transistor.getTechnology().getShapeOfNode(dni);
double widestPoly = 0, widestActive = 0;
for(int i=0; i primErrorInfo = new HashMap();
errorInfo.put(transistor, primErrorInfo);
FixpTransform trans = ni.rotateOut();
Technology tech = ni.getProto().getTechnology();
Poly [] tPolys = tech.getShapeOfNode(ni);
for(Poly tPoly : tPolys)
{
Layer l = tPoly.getLayer();
if (l == null) continue;
l = geometricLayer(l);
Layer.Function fun = l.getFunction();
if (fun == Layer.Function.WELLP && pSubstrateProcess) continue;
if (fun == Layer.Function.WELLN && nSubstrateProcess) continue;
tPoly.setLayer(l);
tPoly.transform(trans);
Point2D[] points = tPoly.getPoints();
for(int i=0; i 0) continue;
realizeNode(transistor, Technology.NodeLayer.MULTICUT_CENTERED, cX, cY,
width, height, angle, null, merge, newCell, null, usePureLayerNodes);
errorInfo.clear();
break;
}
if (errorInfo.size() > 0)
{
String eMsg = "Cell " + newCell.describe(false) + ": Cannot extract Transistor at (" +
TextUtils.formatDistance(ctr.getX()) + "," + TextUtils.formatDistance(ctr.getY()) + ")";
// see if select/well layers failed universally
boolean missingSubstrate = true;
for(PrimitiveNode pnp : errorInfo.keySet())
{
Map primErrorInfo = errorInfo.get(pnp);
boolean substrateHere = false;
for(Layer lay : primErrorInfo.keySet())
{
if (lay.getFunction().isSubstrate()) substrateHere = true;
}
if (!substrateHere) missingSubstrate = false;
}
if (missingSubstrate)
{
eMsg += " because there are no well or select layers (if they are in lower-levels of hierarchy, use Network Preferences to expand them)";
addErrorLog(newCell, eMsg, new EPoint(cX/SCALEFACTOR, cY/SCALEFACTOR));
} else
{
// not a well/select problem: look for smallest failure
double smallestArea = Double.MAX_VALUE;
PrimitiveNode smallestPNP = null;
Layer smallestLayer = null;
Rectangle2D smallestRect = null;
for(PrimitiveNode pnp : errorInfo.keySet())
{
Map primErrorInfo = errorInfo.get(pnp);
for(Layer lay : primErrorInfo.keySet())
{
Rectangle2D errRect = primErrorInfo.get(lay);
double rectSize = errRect.getWidth() * errRect.getHeight();
if (rectSize < smallestArea)
{
smallestArea = rectSize;
smallestPNP = pnp;
smallestLayer = lay;
smallestRect = errRect;
}
}
}
if (smallestPNP != null)
{
eMsg += " because" + smallestPNP.describe(false) + " needs layer " + smallestLayer.getName() + " to run from " +
TextUtils.formatDistance(smallestRect.getMinX()/SCALEFACTOR) + "<=X<=" +
TextUtils.formatDistance(smallestRect.getMaxX()/SCALEFACTOR) + " and " +
TextUtils.formatDistance(smallestRect.getMinY()/SCALEFACTOR) + "<=Y<=" +
TextUtils.formatDistance(smallestRect.getMaxY()/SCALEFACTOR);
}
addErrorLog(newCell, eMsg, new EPoint(cX/SCALEFACTOR, cY/SCALEFACTOR));
}
}
}
}
}
originalMerge.deleteLayer(tempLayer1);
}
/**
* Method to extract a transistor from a non-Manhattan polygon that defines the intersection of poly and active.
* @param poly the outline of poly/active to extract.
* @param transistor the type of transistor to create.
* @param merge the geometry collection to adjust when a transistor is extracted.
* @param originalMerge the original geometry collection (for examination).
* @param newCell the cell in which to create the extracted transistor
*/
private void extractNonManhattanTransistor(PolyBase poly, PrimitiveNode transistor, PolyMerge merge,
PolyMerge originalMerge, Cell newCell, boolean usePureLayerNodes)
{
// determine minimum width of polysilicon
SizeOffset so = transistor.getProtoSizeOffset();
double minWidth = transistor.getDefHeight(ep) - so.getLowYOffset() - so.getHighYOffset();
// reduce the geometry to a skeleton of centerlines
List lines = findCenterlines(poly, tempLayer1, minWidth, merge, originalMerge);
if (lines.size() == 0) return;
// if just one line, it is simply an angled transistor
if (lines.size() == 1)
{
Centerline cl = lines.get(0);
double polySize = cl.start.distance(cl.end);
double activeSize = cl.width;
double cX = (cl.start.getX() + cl.end.getX()) / 2;
double cY = (cl.start.getY() + cl.end.getY()) / 2;
double sX = polySize + scaleUp(so.getLowXOffset() + so.getHighXOffset());
double sY = activeSize + scaleUp(so.getLowYOffset() + so.getHighYOffset());
realizeNode(transistor, Technology.NodeLayer.MULTICUT_CENTERED, cX, cY, sX, sY,
cl.angle, null, merge, newCell, null, usePureLayerNodes);
return;
}
// serpentine transistor: organize the lines into an array of points
EPoint [] points = new EPoint[lines.size()+1];
for(Centerline cl : lines)
{
cl.handled = false;
}
Centerline firstCL = lines.get(0);
firstCL.handled = true;
points[0] = new EPoint(firstCL.start.getX(), firstCL.start.getY());
points[1] = new EPoint(firstCL.end.getX(), firstCL.end.getY());
int pointsSeen = 2;
while (pointsSeen < points.length)
{
boolean added = false;
for(Centerline cl : lines)
{
if (cl.handled) continue;
EPoint start = new EPoint(cl.start.getX(), cl.start.getY());
EPoint end = new EPoint(cl.end.getX(), cl.end.getY());
if (start.equals(points[0]))
{
// insert "end" point at start
for(int i=pointsSeen; i>0; i--)
points[i] = points[i-1];
points[0] = end;
pointsSeen++;
cl.handled = true;
added = true;
break;
}
if (end.equals(points[0]))
{
// insert "start" point at start
for(int i=pointsSeen; i>0; i--)
points[i] = points[i-1];
points[0] = start;
pointsSeen++;
cl.handled = true;
added = true;
break;
}
if (start.equals(points[pointsSeen-1]))
{
// add "end" at the end
points[pointsSeen++] = end;
cl.handled = true;
added = true;
break;
}
if (end.equals(points[pointsSeen-1]))
{
// add "start" at the end
points[pointsSeen++] = start;
cl.handled = true;
added = true;
break;
}
}
if (!added) break;
}
// make sure all points are handled
if (pointsSeen != points.length) return;
// compute information about the transistor and create it
double lX = points[0].getX(), hX = points[0].getX();
double lY = points[0].getY(), hY = points[0].getY();
for(int i=1; i hX) hX = points[i].getX();
if (points[i].getY() < lY) lY = points[i].getY();
if (points[i].getY() > hY) hY = points[i].getY();
}
double cX = (lX + hX) / 2;
double cY = (lY + hY) / 2;
for(int i=0; i extendableLayers = new ArrayList();
for (Layer layer : merge.getKeySet())
{
ArcProto ap = arcsForLayer.get(layer);
if (ap == null) continue;
extendableLayers.add(layer);
}
// gather everything to extend
int totExtensions = 0;
Map> geomToExtend = new HashMap>();
int soFar = 0;
for (Layer layer : extendableLayers)
{
List polyList = getMergePolys(merge, layer, null);
geomToExtend.put(layer, polyList);
totExtensions += polyList.size();
soFar++;
if (!recursive) Job.getUserInterface().setProgressValue(soFar * 100 / extendableLayers.size());
}
if (!recursive) Job.getUserInterface().setProgressValue(0);
soFar = 0;
for (Layer layer : extendableLayers)
{
ArcProto ap = arcsForLayer.get(layer);
if (ap == null) continue;
double wid = ap.getDefaultLambdaBaseWidth(ep);
double arcLayerWidth = ap.getDefaultInst(ep).getCoordExtendOverMin().add(ap.getLayerExtend(layer)).multiply(2).getLambda();
List polyList = geomToExtend.get(layer);
for(PolyBase poly : polyList)
{
soFar++;
if (!recursive) Job.getUserInterface().setProgressValue(soFar * 100 / totExtensions);
// find out what this polygon touches
Map netsThatTouch = getNetsThatTouch(poly, newCell, justExtend);
if (netsThatTouch == null) continue;
// make a list of port/arc ends that touch this polygon
List