/* -*- 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 Sun Microsystems and Static Free Software
*
* 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.Main;
import com.sun.electric.database.EditingPreferences;
import com.sun.electric.database.geometry.DBMath;
import com.sun.electric.database.geometry.Dimension2D;
import com.sun.electric.database.geometry.EPoint;
import com.sun.electric.database.geometry.ERectangle;
import com.sun.electric.database.geometry.GenMath;
import com.sun.electric.database.geometry.GeometryHandler;
import com.sun.electric.database.geometry.Orientation;
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.geometry.GenMath.MutableBoolean;
import com.sun.electric.database.geometry.GenMath.MutableInteger;
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.*;
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 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.*;
import java.util.*;
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 nonmanhattan 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<Layer.Function,Layer> 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<Layer,ArcProto> arcsForLayer;
/** map of extracted cells */ private Map<Cell,Cell> convertedCells;
/** map of cut layers to lists of polygons on that layer */ private Map<Layer,CutInfo> allCutLayers;
/** set of pure-layer nodes that are not processed */ private Set<PrimitiveNode> ignoreNodes;
/** set of contacts that are not used for extraction */ private Set<PrimitiveNode> bogusContacts;
/** PrimitiveNodes for pdiff and ndiff */ private PrimitiveNode diffNode, pDiffNode, nDiffNode;
/** list of Exports to restore after extraction */ private List<Export> exportsToRestore;
/** auto-generated exports that may need better names */ private List<Export> 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<ERectangle> addedRectangles;
/** debugging: list of objects created */ private List<ERectangle> addedLines;
/** list of exported pins to realize at the end */ private List<ExportedPin> 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;
/** Grid alignment for edges */ private Dimension2D 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 boolean approximateCuts;
private boolean flattenPcells;
/** debugging: list of objects created */ private List<List<ERectangle>> addedBatchRectangles;
/** debugging: list of objects created */ private List<List<ERectangle>> addedBatchLines;
/** debugging: list of objects created */ private List<String> 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();
approximateCuts = Extract.isApproximateCuts();
flattenPcells = Extract.isFlattenPcells();
startJob();
}
public boolean doIt() throws JobException
{
// get pattern for matching cells to expand
Pattern pat = null;
if (expansionPattern.length() > 0)
{
try
{
pat = Pattern.compile(expansionPattern, Pattern.CASE_INSENSITIVE);
} catch(PatternSyntaxException e)
{
System.out.println("Pattern syntax error on '" + expansionPattern + "': " + e.getMessage());
}
}
if (DEBUGSTEPS)
{
addedBatchRectangles = new ArrayList<List<ERectangle>>();
addedBatchLines = new ArrayList<List<ERectangle>>();
addedBatchNames = new ArrayList<String>();
}
Job.getUserInterface().startProgressDialog("Extracting", null);
Connectivity c = new Connectivity(cell, this, errorLogger, smallestPolygonSize, activeHandling,
gridAlignExtraction, approximateCuts, recursive, pat);
if (recursive) c.totalCells = c.countExtracted(cell, pat, flattenPcells);
c.cellsExtracted = 0;
newCell = c.doExtract(cell, recursive, pat, flattenPcells, 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;
jf = (JFrame) Main.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<NodeInst> 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 errorlongger
* @param smallestPolygonSize the smallest polygon size
* @param activeHandling ?
* @param gridAlignExtraction true to align extraction to some the technology grid
* @param approximateCuts approximate cuts
* @param recursive run recursively
* @param pat ?
*/
private Connectivity(Cell cell, Job j, ErrorLogger eLog, double smallestPolygonSize, int activeHandling,
boolean gridAlignExtraction, boolean approximateCuts, boolean recursive, Pattern pat)
{
this.approximateCuts = approximateCuts;
this.recursive = recursive;
tech = cell.getTechnology();
convertedCells = new HashMap<Cell,Cell>();
smallestPoly = (SCALEFACTOR * SCALEFACTOR) * smallestPolygonSize;
bogusContacts = new HashSet<PrimitiveNode>();
errorLogger = eLog;
job = j;
this.gridAlignExtraction = gridAlignExtraction;
double scaledResolution = tech.getFactoryScaledResolution();
alignment = new Dimension2D.Double(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<PrimitiveNode>();
for(Iterator<PrimitiveNode> 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<nLays.length; i++)
{
Technology.NodeLayer nLay = nLays[i];
Layer.Function fun = nLay.getLayer().getFunction();
if (fun == Layer.Function.UNKNOWN || fun == Layer.Function.OVERGLASS ||
fun == Layer.Function.GUARD || fun == Layer.Function.ISOLATION ||
fun == Layer.Function.BUS || fun == Layer.Function.ART ||
fun == Layer.Function.CONTROL || fun == Layer.Function.TILENOT) continue;
validLayers = true;
}
if (!validLayers) ignoreNodes.add(np);
// determine diffusion nodes
Layer layer = np.getLayerIterator().next();
if (layer.getFunction() == Layer.Function.DIFF)
diffNode = np;
if (layer.getFunction() == Layer.Function.DIFFN)
nDiffNode = np;
if (layer.getFunction() == Layer.Function.DIFFP)
pDiffNode = np;
}
// determine if this is a "P-well" or "N-well" process
findMissingWells(cell, recursive, pat, activeHandling);
// find important layers
polyLayer = null;
activeLayer = pActiveLayer = nActiveLayer = null;
realPActiveLayer = realNActiveLayer = null;
pSelectLayer = nSelectLayer = null;
wellLayer = substrateLayer = null;
for(Iterator<Layer> 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) // psubstrate
{
if (wellLayer == null && fun == Layer.Function.WELLN) wellLayer = layer;
if (substrateLayer == null && fun == Layer.Function.WELLP) substrateLayer = layer;
}
if (nSubstrateProcess) // nsubstrate
{
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 (pActiveLayer == null || nActiveLayer == null && activeLayer != null) {
unifyActive = true;
pActiveLayer = nActiveLayer = activeLayer;
} else {
unifyActive = false;
pActiveLayer = pActiveLayer.getNonPseudoLayer();
nActiveLayer = nActiveLayer.getNonPseudoLayer();
}
// figure out which arcs to use for a layer
arcsForLayer = new HashMap<Layer,ArcProto>();
for(Iterator<Layer> 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<ArcProto> 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<Layer.Function,Layer>();
for(Iterator<Layer> 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<EPoint> pointList = new ArrayList<EPoint>();
for(EPoint p : pList)
pointList.add(p);
errorLogger.logMessage(msg, pointList, cell, -1, true);
System.out.println(msg);
}
private int countExtracted(Cell oldCell, Pattern pat, boolean flattenPcells)
{
int numExtracted = 1;
for(Iterator<NodeInst> 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, pat, flattenPcells)) continue;
Cell convertedCell = convertedCells.get(subCell);
if (convertedCell == null)
{
numExtracted += countExtracted(subCell, pat, flattenPcells);
}
}
}
return numExtracted;
}
/**
* Method to determine whether to flatten a cell.
* @param cell the cell in question.
* @param pat the pattern 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, Pattern pat, boolean flattenPcells)
{
// do not recurse if this subcell will be expanded
if (pat != null)
{
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<endPart.length(); i++)
{
char ch = endPart.charAt(i);
if (ch == '{') break;
if (!TextUtils.isDigit(ch)) return false;
}
return true;
}
}
return false;
}
/**
* Top-level method in extracting connectivity from a Cell.
* A new version of the cell is created that has real nodes (transistors, contacts) and arcs.
* This cell should have pure-layer nodes which will be converted.
*/
private Cell doExtract(Cell oldCell, boolean recursive, Pattern pat, boolean flattenPcells, boolean top, Job job,
List<List<ERectangle>> addedBatchRectangles, List<List<ERectangle>> addedBatchLines, List<String> addedBatchNames)
{
if (recursive)
{
// first see if subcells need to be converted
for(Iterator<NodeInst> 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, pat, flattenPcells)) continue;
Cell convertedCell = convertedCells.get(subCell);
if (convertedCell == null)
{
Cell result = doExtract(subCell, recursive, pat, flattenPcells, 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(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<Cell> expandedCells = new HashSet<Cell>();
exportsToRestore = new ArrayList<Export>();
generatedExports = new ArrayList<Export>();
pinsForLater = new ArrayList<ExportedPin>();
allCutLayers = new TreeMap<Layer,CutInfo>();
extractCell(oldCell, newCell, pat, 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 AffineTransform());
// start by extracting vias
initDebugging();
if (!startSection(oldCell, "Extracting vias...")) return null; // aborted
if (!extractVias(merge, originalMerge, oldCell, newCell)) return null; // aborted
termDebugging(addedBatchRectangles, addedBatchLines, addedBatchNames, "Vias");
// now extract transistors
initDebugging();
if (!startSection(oldCell, "Extracting transistors...")) return null; // aborted
extractTransistors(merge, originalMerge, newCell);
termDebugging(addedBatchRectangles, addedBatchLines, addedBatchNames, "Transistors");
if (Extract.isUsePureLayerNodes()) {
// dump back in original routing layers
if (!startSection(oldCell, "Adding in original routing layers...")) return null;
addInRoutingLayers(oldCell, newCell, merge, originalMerge);
} else {
// extend geometry that sticks out in space
/*
initDebugging();
if (!startSection(oldCell, "Extracting extensions...")) return null; // aborted
extendGeometry(merge, originalMerge, newCell, true);
termDebugging(addedBatchRectangles, addedBatchLines, addedBatchNames, "StickOuts");
*/
// look for wires and pins
initDebugging();
if (!startSection(oldCell, "Extracting wires...")) return null; // aborted
if (makeWires(merge, originalMerge, newCell)) 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);
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);
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;
AffineTransform shrink = new AffineTransform(shrinkage, 0, 0, shrinkage, 0, 0);
originalUnscaledMerge.addMerge(originalMerge, shrink);
Set<ArcInst> allArcs = null;
allArcs = new HashSet<ArcInst>();
for(Iterator<ArcInst> 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 hashmap
// might take up a lot of memory.
AutoOptions prefs = new AutoOptions();
prefs.createExports = true;
AutoStitch.runAutoStitch(newCell, null, null, job, originalUnscaledMerge, null, false, true, 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<ArcInst> it = newCell.getArcs(); it.hasNext(); ) {
ArcInst ai = it.next();
if (allArcs.contains(ai)) continue;
Rectangle2D bounds = ai.getBounds();
if (bounds.getMinX() % alignment.getWidth() != 0 || bounds.getMinY() % alignment.getHeight() != 0 ||
bounds.getMaxX() % alignment.getWidth() != 0 || bounds.getMaxY() % alignment.getHeight() != 0) {
// replace
Connection head = ai.getHead();
Connection tail = ai.getTail();
ArcInst newAi = ArcInst.makeInstanceBase(Generic.tech().universal_arc, 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<ArcInst> 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<ERectangle>();
addedLines = new ArrayList<ERectangle>();
}
}
private void termDebugging(List<List<ERectangle>> addedBatchRectangles,
List<List<ERectangle>> addedBatchLines, List<String> addedBatchNames, String descr)
{
if (DEBUGSTEPS)
{
addedBatchRectangles.add(addedRectangles);
addedBatchLines.add(addedLines);
addedBatchNames.add(descr);
}
}
private static class ExportedPin
{
Point2D location;
NodeInst ni;
AffineTransform trans;
ExportedPin(NodeInst ni, Point2D location, AffineTransform 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 pat a pattern 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, Pattern pat, boolean flattenPcells, Set<Cell> expandedCells,
PolyMerge merge, PolyMerge selectMerge, AffineTransform prevTrans, Orientation orient)
{
Map<NodeInst,NodeInst> newNodes = new HashMap<NodeInst,NodeInst>();
int totalNodes = oldCell.getNumNodes();
Dimension2D alignementToGrid = newCell.getEditingPreferences().getAlignmentToGrid();
// first get select, so we can determine proper active type
if (!unifyActive && !ignoreActiveSelectWell)
{
for (Iterator<NodeInst> nIt = oldCell.getNodes(); nIt.hasNext(); )
{
NodeInst ni = nIt.next();
if (ni.isCellInstance()) continue;
Poly [] polys = tech.getShapeOfNode(ni);
for(int j=0; j<polys.length; j++)
{
Poly poly = polys[j];
// get the layer for the geometry
Layer layer = poly.getLayer();
if (layer == null) continue;
// make sure the geometric database is made up of proper layers
layer = geometricLayer(layer);
if (layer.getFunction() != Layer.Function.IMPLANTN && layer.getFunction() != Layer.Function.IMPLANTP) continue;
// selectMerge has non-scaled-up coords, and has all geom coords relative to top level
AffineTransform trans = ni.rotateOut(prevTrans);
poly.transform(trans);
selectMerge.add(layer, poly);
}
}
}
int soFar = 0;
for(Iterator<NodeInst> 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, pat, flattenPcells);
if (flatIt)
{
// expanding the subcell
expandedCells.add(subCell);
AffineTransform subTrans = ni.translateOut(ni.rotateOut(prevTrans));
Orientation or = orient.concatenate(ni.getOrient());
extractCell(subCell, newCell, pat, 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, 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<Export> it = ni.getExports(); it.hasNext(); )
{
Export e = it.next();
PortInst pi = newNi.findPortInstFromProto(e.getOriginalPort().getPortProto());
Export.newInstance(newCell, pi, e.getName());
}
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
AffineTransform trans = ni.rotateOut(prevTrans);
Poly [] polys = tech.getShapeOfNode(ni);
for(int j=0; j<polys.length; j++)
{
Poly poly = polys[j];
// get the layer for the geometry
Layer layer = poly.getLayer();
if (layer == null) continue;
// make sure the geometric database is made up of proper layers
layer = geometricLayer(layer);
// finally add the geometry to the merge
poly.transform(trans);
Point2D [] points = poly.getPoints();
if (gridAlignExtraction)
{
Point2D hold = new Point2D.Double();
for(int i=0; i<points.length; i++)
{
hold.setLocation(points[i]);
DBMath.gridAlign(hold, alignementToGrid);
poly.setPoint(i, hold.getX(), hold.getY());
}
} else
{
// grow the polygon to account for rounding problems
if (growABit)
{
double growth = DBMath.getEpsilon()/2;
Point2D polyCtr = poly.getCenter();
for(int i=0; i<points.length; i++)
{
double x = points[i].getX();
double y = points[i].getY();
if (x < polyCtr.getX()) x -= growth; else x += growth;
if (y < polyCtr.getY()) y -= growth; else y += growth;
poly.setPoint(i, x, y);
}
}
}
// check overlap with selectMerge here, after poly has been rotated up to top level, but before scaling
if (layer.getFunction() == Layer.Function.DIFFN) {
// make sure n-diffusion is in n-select
if (selectMerge.contains(pSelectLayer, poly)) {
// switch it pactive
layer = pActiveLayer;
}
}
if (layer.getFunction() == Layer.Function.DIFFP) {
// make sure p-diffusion is in p-select
if (selectMerge.contains(nSelectLayer, poly)) {
// switch it nactive
layer = nActiveLayer;
}
}
for(int i=0; i<points.length; i++)
poly.setPoint(i, scaleUp(points[i].getX()), scaleUp(points[i].getY()));
if (layer.getFunction().isContact())
{
// cut layers are stored in lists because merging them is too expensive and pointless
CutInfo cInfo = allCutLayers.get(layer);
if (cInfo == null) allCutLayers.put(layer, cInfo = new CutInfo());
// see if the cut is already there
boolean found = false;
for(RTNode.Search sea = new RTNode.Search(poly.getBounds2D(), cInfo.getRTree(), true); sea.hasNext(); )
{
CutBound cBound = (CutBound)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<Export> it = ni.getExports(); it.hasNext(); )
{
Export e = it.next();
exportsToRestore.add(e);
}
}
// throw all arcs into the new cell, too
for(Iterator<ArcInst> 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(), ai.getLambdaBaseWidth(), pi1, pi2,
headLocation, tailLocation, ai.getName());
}
// throw all cell text into the new cell, too
for(Iterator<Variable> 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 pat an exclusion pattern for cell names.
*/
private void findMissingWells(Cell cell, boolean recursive, Pattern pat, int activeHandling)
{
hasWell = hasPWell = hasNWell = false;
haveNActive = havePActive = false;
recurseMissingWells(cell, recursive, pat);
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 pat an exclusion pattern for cell names.
*/
private void recurseMissingWells(Cell cell, boolean recursive, Pattern pat)
{
examineCellForMissingWells(cell);
if (recursive)
{
// now see if subcells need to be converted
for(Iterator<NodeInst> 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
if (pat != null)
{
Matcher mat = pat.matcher(subCell.noLibDescribe());
if (mat.find()) continue;
}
Cell convertedCell = convertedCells.get(subCell);
if (convertedCell == null)
{
recurseMissingWells(subCell, recursive, pat);
}
}
}
}
}
/**
* 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<NodeInst> 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<polys.length; i++)
{
Layer layer = polys[i].getLayer();
if (layer == null) continue;
if (!layer.getFunction().isDiff()) continue;
if (layer.getFunction() == Layer.Function.DIFFN) haveNActive = true;
if (layer.getFunction() == Layer.Function.DIFFP) havePActive = true;
}
if (haveNActive && havePActive) break;
}
for(Iterator<NodeInst> 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<nLayers.length; i++)
{
NodeLayer nLay = nLayers[i];
Layer layer = nLay.getLayer();
Layer.Function fun = layer.getFunction();
if (fun == Layer.Function.WELL) hasWell = true;
if (fun == Layer.Function.WELLP) hasPWell = true;
if (fun == Layer.Function.WELLN) hasNWell = true;
if (layer.getFunction().isDiff())
{
if (layer.getFunction() == Layer.Function.DIFFN) haveNActive = true;
if (layer.getFunction() == Layer.Function.DIFFP) havePActive = true;
continue;
}
}
continue;
}
}
// examine all arcs
for(Iterator<ArcInst> aIt = cell.getArcs(); aIt.hasNext(); )
{
ArcInst ai = aIt.next();
for(int i=0; i<ai.getProto().getNumArcLayers(); i++)
{
Layer layer = ai.getProto().getLayer(i);
Layer.Function fun = layer.getFunction();
if (fun == Layer.Function.WELL) hasWell = true;
if (fun == Layer.Function.WELLP) hasPWell = true;
if (fun == Layer.Function.WELLN) hasNWell = true;
if (layer.getFunction().isDiff())
{
if (layer.getFunction() == Layer.Function.DIFFN) haveNActive = true;
if (layer.getFunction() == Layer.Function.DIFFP) havePActive = true;
continue;
}
}
}
}
/********************************************** WIRE EXTRACTION **********************************************/
private boolean isOnGrid(double value, double grid)
{
if (grid == 0) return true;
long x = Math.round(value / grid);
return x * grid == value;
}
/**
* Method to extract wires from the merge.
* @param merge the merged geometry that remains (after contacts and transistors are extracted).
* @param originalMerge the original merge with all geometry.
* @param newCell the new Cell in which wires are placed.
* @return true on error.
*/
private boolean makeWires(PolyMerge merge, PolyMerge originalMerge, Cell newCell)
{
// make a list of polygons that could be turned into wires
int totPolys = 0;
Map<Layer,List<PolyBase>> geomToWire = new TreeMap<Layer,List<PolyBase>>();
for (Layer layer : merge.getKeySet())
{
Layer.Function fun = layer.getFunction();
if (fun.isDiff() || fun.isPoly() || fun.isMetal())
{
List<PolyBase> 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<Layer> allLayers = geomToWire.keySet();
for (Layer layer : allLayers)
{
// examine the geometry on the layer
List<PolyBase> 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());
List<Centerline> 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(), 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 geom
for (Layer layer : allLayers)
{
List<PolyBase> polyList = getMergePolys(merge, layer, null);
for(PolyBase poly : polyList)
{
ArcProto ap = findArcProtoForPoly(layer, poly, originalMerge);
if (ap == null) continue;
PrimitiveNode pin = ap.findPinProto();
List<NodeInst> 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<RTBounds> it = newCell.searchIterator(searchBound); it.hasNext(); )
{
RTBounds 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<Connection> 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, ai.getLambdaBaseWidth(), fPi, oConn.getPortInst(),
conn.getLocation(), oConn.getLocation(), null);
} else {
oConn = ai.getHead();
newAi = ArcInst.makeInstanceBase(ap, 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<PolyBase> 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 proto 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, 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<PrimitiveNode> 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<NodeInst> it = cell.getNodes(); it.hasNext(); ) {
NodeInst ni = it.next();
if (ni.isCellInstance()) continue;
Poly [] polys = tech.getShapeOfNode(ni);
for(int j=0; j<polys.length; j++)
{
Poly poly = polys[j];
// get the layer for the geometry
Layer layer = poly.getLayer();
if (layer == null) continue;
// make sure the geometric database is made up of proper layers
layer = geometricLayer(layer);
if (layer.getFunction() != Layer.Function.IMPLANTN && layer.getFunction() != Layer.Function.IMPLANTP) continue;
merge.add(layer, poly);
}
}
// now fix any active nodes
for (Iterator<NodeInst> 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 pactive
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 nactive
newType = nDiffNode;
break;
}
}
}
if (newType == null) continue;
NodeInst newNi = NodeInst.newInstance(newType, 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 coords
for (int i=0; i<origPoints.length; i++) {
points[i] = new Point2D.Double(origPoints[i].getX()+ni.getAnchorCenterX(), origPoints[i].getY()+ni.getAnchorCenterY());
}
newNi.setTrace(points);
}
ni.kill();
}
}
}
/**
* Method to figure out which ArcProto to use for a polygon on a layer.
* In the case of Active layers, it examines the well and select layers to figure out
* which arctive arc to use.
* @param layer the layer of the polygon.
* @param poly the polygon
* @param originalMerge the merged data for the cell
* @return the ArcProto to use (null if none can be found).
*/
private ArcProto findArcProtoForPoly(Layer layer, PolyBase poly, PolyMerge originalMerge)
{
/*
Layer.Function fun = layer.getFunction();
if (fun.isPoly() || fun.isMetal()) return arcsForLayer.get(layer);
if (!fun.isDiff()) return null;
// must further differentiate the active arcs...find implants
Layer wellP = null, wellN = null, selectP = null, selectN = null;
for(Layer l : originalMerge.getKeySet())
{
if (l.getFunction() == Layer.Function.WELLP) wellP = l;
if (l.getFunction() == Layer.Function.WELLN) wellN = l;
if (l.getFunction() == Layer.Function.IMPLANTP) selectP = l;
if (l.getFunction() == Layer.Function.IMPLANTN) selectN = l;
}
ArcProto.Function neededFunction = null;
if (selectP != null && originalMerge.intersects(selectP, poly))
{
// Active in P-Select could either be a P-Active arc or a P-Well arc
neededFunction = ArcProto.Function.DIFFP;
if (wellP == null)
{
// a P-Well process: just look for the absense of N-Well surround
if (wellN == null || !originalMerge.intersects(wellN, poly)) neededFunction = ArcProto.Function.DIFFS;
} else
{
if (originalMerge.intersects(wellP, poly)) neededFunction = ArcProto.Function.DIFFS;
}
} else if (selectN != null && originalMerge.intersects(selectN, poly))
{
// Active in N-Select could either be a N-Active arc or a N-Well arc
neededFunction = ArcProto.Function.DIFFN;
if (wellN == null)
{
// a N-Well process: just look for the absense of P-Well surround
if (wellP == null || !originalMerge.intersects(wellP, poly)) neededFunction = ArcProto.Function.DIFFW;
} else
{
if (originalMerge.intersects(wellN, poly)) neededFunction = ArcProto.Function.DIFFW;
}
}
// now find the arc with the desired function
for(Iterator<ArcProto> 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<Layer> requiredLayers = new ArrayList<Layer>();
ArrayList<Layer> requiredAbsentLayers = new ArrayList<Layer>();
// must further differentiate the active arcs...find implants
Layer wellP = null, wellN = null, selectP = null, selectN = 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<ArcProto> aIt = tech.getArcs(); aIt.hasNext(); )
{
ArcProto ap = aIt.next();
List<Layer> apLayers = new ArrayList<Layer>();
for (Iterator<Layer> 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<RTBounds> it = newCell.searchIterator(bounds); it.hasNext(); )
{
RTBounds geom = it.next();
if (!(geom instanceof NodeInst)) continue;
NodeInst ni = (NodeInst)geom;
for(Iterator<PortInst> 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<RTBounds> it = newCell.searchIterator(bounds); it.hasNext(); )
{
RTBounds 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<PortProto> 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-manhatten
}
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<PortInst> 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<points.length; i++)
{
int last = i-1;
if (last < 0) last = points.length-1;
Point2D portLineFrom = points[last];
Point2D portLineTo = points[i];
Point2D interPt = null;
if (portLineFrom.equals(portLineTo))
{
interPt = GenMath.intersect(cl.startUnscaled, cl.angle, portLineFrom, (cl.angle+900)%3600);
} else
{
int angPortLine = GenMath.figureAngle(portLineFrom, portLineTo);
interPt = GenMath.intersect(portLineFrom, angPortLine, cl.startUnscaled, cl.angle);
if (interPt != null)
{
if (interPt.getX() < Math.min(portLineFrom.getX(), portLineTo.getX()) ||
interPt.getX() > 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 offgrid, 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.getFactoryDefaultLambdaBaseWidth();
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 offgrid, 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<PortInst> findPortInstsTouchingPoint(Point2D pt, Layer layer, Cell newCell, ArcProto ap)
{
List<PortInst> touchingNodes = new ArrayList<PortInst>();
boolean mightCreateExports = false;
Rectangle2D checkBounds = new Rectangle2D.Double(pt.getX(), pt.getY(), 0, 0);
for(Iterator<RTBounds> it = newCell.searchIterator(checkBounds); it.hasNext(); )
{
RTBounds geom = it.next();
if (!(geom instanceof NodeInst)) continue;
NodeInst ni = (NodeInst)geom;
if (ni.isCellInstance())
{
boolean found = false;
for(Iterator<PortInst> 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
{
// nonpins can have any touching and connecting layer
Poly [] polys = tech.getShapeOfNode(ni, true, true, null);
AffineTransform trans = ni.rotateOut();
for(int i=0; i<polys.length; i++)
{
Poly oPoly = polys[i];
Layer oLayer = geometricLayer(oPoly.getLayer());
if (layer != oLayer) continue;
oPoly.transform(trans);
// do the polys touch?
if (oPoly.contains(pt))
{
PortInst touchingPi = findPortInstClosestToPoly(ni, (PrimitivePort)oPoly.getPort(), pt);
if (touchingPi == null)
{
addErrorLog(newCell, "Can't find port for "+ni+" and "+oPoly.getPort(),
new EPoint(pt.getX(), pt.getY()));
continue;
}
touchingNodes.add(touchingPi);
break;
}
}
}
}
// if no ports were found but there were cells, should create new exports in the cells
if (touchingNodes.size() == 0 && mightCreateExports)
{
// can we create an export on the cell?
PortInst pi = makePort(newCell, layer, pt);
if (pi != null)
touchingNodes.add(pi);
}
return touchingNodes;
}
/**
* Method to connect to a given location and layer.
* @param cell the cell in which this location/layer exists.
* @param layer the layer on which to connect.
* @param pt the location in the Cell for the connection.
* @return a PortInst on a node in the Cell (null if it cannot be found).
*/
private PortInst makePort(Cell cell, Layer layer, Point2D pt)
{
Rectangle2D checkBounds = new Rectangle2D.Double(pt.getX(), pt.getY(), 0, 0);
// first look for port on primitive geometry in cell
for (Iterator<RTBounds> it = cell.searchIterator(checkBounds); it.hasNext();)
{
RTBounds geom = it.next();
if (!(geom instanceof NodeInst)) continue;
NodeInst subNi = (NodeInst)geom;
if (subNi.isCellInstance()) continue;
Technology tech = subNi.getProto().getTechnology();
AffineTransform trans = subNi.rotateOut();
Poly [] polyList = tech.getShapeOfNode(subNi, true, true, null);
for(int i=0; i<polyList.length; i++)
{
Poly poly = polyList[i];
if (poly.getPort() == null) continue;
if (geometricLayer(poly.getLayer()) != layer) continue;
poly.transform(trans);
if (poly.contains(pt))
{
// found polygon that touches the point. Make the export
PortInst foundPi = findPortInstClosestToPoly(subNi, (PrimitivePort)poly.getPort(), pt);
if (foundPi != null) return foundPi;
}
}
}
// nothing found, now push down into subcells
for(Iterator<RTBounds> it = cell.searchIterator(checkBounds); it.hasNext(); )
{
RTBounds geom = it.next();
if (!(geom instanceof NodeInst)) continue;
NodeInst subNi = (NodeInst)geom;
PortInst foundPi = null;
if (subNi.isCellInstance())
{
AffineTransform 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<Export> 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<String> 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, PortProto.class, true, true);
Export e = Export.newInstance(subCell, pi, exportName);
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)
{
// make a list of all via/cut layers in the technology and count the number of vias/cuts
int totalCuts = 0;
List<Layer> layers = new ArrayList<Layer>();
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<NodeInst> contactNodes = new ArrayList<NodeInst>();
// examine all vias/cuts for possible contacts
EditingPreferences ep = oldCell.getEditingPreferences();
int soFar = 0;
for (Layer layer : layers)
{
// compute the possible via nodes that this layer could become
List<PossibleVia> possibleVias = findPossibleVias(layer, ep);
// make a list of all necessary layers
Set<Layer> layersToExamine = new TreeSet<Layer>();
for(PossibleVia pv : possibleVias)
{
for(int i=0; i<pv.layers.length; i++)
layersToExamine.add(pv.layers[i]);
}
// get all of the geometry on the cut/via layer
CutInfo cInfo = allCutLayers.get(layer);
cInfo.sortCuts();
// the new way to extract vias
List<PolyBase> cutsNotExtracted = new ArrayList<PolyBase>();
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<Layer> layersPresent = new TreeSet<Layer>();
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<Layer> missingLayers = null;
for(int i=0; i<pv.layers.length; i++)
{
if (!layersPresent.contains(pv.layers[i]))
{
if (ignorePWell && pv.layers[i].getFunction() == Layer.Function.WELLP) continue;
if (ignoreNWell && pv.layers[i].getFunction() == Layer.Function.WELLN) continue;
if (missingLayers == null) missingLayers = new ArrayList<Layer>();
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<primLayers.length; i++)
{
if (primLayers[i].getLayer().getFunction().isDiff()) activeCut = true;
if (primLayers[i].getLayer().getFunction().isPoly()) polyCut = true;
}
if (activeCut && polyCut) activeCut = polyCut = false;
// look for other cuts in the vicinity
Set<PolyBase> cutsInArea = new HashSet<PolyBase>();
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(RTNode.Search sea = new RTNode.Search(searchArea, cInfo.getRTree(), true); sea.hasNext(); )
{
CutBound cBound = (CutBound)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<pv.layers.length; i++)
{
Layer lay = pv.layers[i];
if (lay.getFunction().isMetal())
{
if (!originalMerge.contains(lay, layerPoly)) { fits = false; break; }
}
}
if (!fits) continue;
// accumulate this cut
multiCutBounds = newMultiCutBounds;
cutsInArea.add(cBound.cut);
foundMore = true;
}
}
// reduce via array to rectangle
if (xspacing == 0) xspacing = cutLimit;
if (yspacing == 0) yspacing = cutLimit;
Set<PolyBase> 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, 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, 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, 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);
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 (!Extract.isUsePureLayerNodes())
{
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<PolyBase> getLargestRectangleOfVias(Set<PolyBase> 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<numViasWide; x++) {
Arrays.fill(viaMap[x], null);
}
// populate map
for (PolyBase via : vias) {
int x = (int)((via.getCenterX() - bounds.getMinX()) / xspacing);
int y = (int)((via.getCenterY() - bounds.getMinY()) / yspacing);
viaMap[x][y] = via;
if (via == initialVia) {
nomX = x; nomY = y;
}
}
// find maxY and minY from initial via
int maxY = nomY, minY = nomY;
boolean initial = true;
for (int x=0; x<numViasWide; x++) {
if (viaMap[x][nomY] == null) continue; // skip this column, will be taken into account in X max/min
// max
int colMaxY = nomY;
for (int y=nomY; y<numViasHigh; y++) {
if (viaMap[x][y] == null) break;
colMaxY = y;
}
if (initial) maxY = colMaxY; // initial column
else if (colMaxY < maxY) maxY = colMaxY;
// min
int colMinY = nomY;
for (int y=nomY; y>=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<numViasHigh; y++) {
if (viaMap[nomX][y] == null) continue; // skip this row, will be taken into account in Y max/min
// max
int colMaxX = nomX;
for (int x=nomX; x<numViasWide; x++) {
if (viaMap[x][y] == null) break;
colMaxX = x;
}
if (initial) maxX = colMaxX; // initial row
else if (colMaxX < maxX) maxX = colMaxX;
// min
int colMinX = nomX;
for (int x=nomX; x>=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<PolyBase> rectVias = new HashSet<PolyBase>();
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<NodeInst> contactNodes, boolean activeCut, boolean polyCut, Set<PolyBase> 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);
return null;
}
private NodeInst makeDummyNodeInst(PrimitiveNode pNp, EPoint ctr, double sX, double sY, Orientation orient, int cutVariation)
{
NodeInst ni = NodeInst.makeDummyInstance(pNp, 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));
return ni;
}
private PolyBase dummyNodeFits(NodeInst ni, PolyMerge merge, boolean activeCut, boolean polyCut, Set<PolyBase> cutsInArea)
{
AffineTransform trans = ni.rotateOut();
Technology tech = ni.getProto().getTechnology();
Poly [] polys = tech.getShapeOfNode(ni);
double biggestArea = 0;
Poly biggestPoly = null;
List<PolyBase> cutsFound = null;
if (!approximateCuts) cutsFound = new ArrayList<PolyBase>();
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<points.length; i++)
poly.setPoint(i, scaleUp(points[i].getX()), scaleUp(points[i].getY()));
if (!merge.contains(l, poly))
return poly;
}
// special case: some substrate contacts in some techs do not have the substrate layer (since often the original geom
// also does not have the substrate layer). To prevent these from being used as regular contacts,
// do an extra check here
PrimitiveNode pn = (PrimitiveNode)ni.getProto();
PolyBase testPoly = new PolyBase(new Point2D[]{
new Point2D.Double(scaleUp(ni.getAnchorCenterX())-1, scaleUp(ni.getAnchorCenterY())-1),
new Point2D.Double(scaleUp(ni.getAnchorCenterX())-1, scaleUp(ni.getAnchorCenterY())+1),
new Point2D.Double(scaleUp(ni.getAnchorCenterX())+1, scaleUp(ni.getAnchorCenterY())+1),
new Point2D.Double(scaleUp(ni.getAnchorCenterX())+1, scaleUp(ni.getAnchorCenterY())-1)
});
testPoly.setLayer(wellLayer);
if (pn.getFunction() == PrimitiveNode.Function.SUBSTRATE) // defines a substrate contact
{
// make sure that substrate contact is not being placed on top of Well (Nwell for P-substrate process)
if (wellLayer != null)
{
if (merge.contains(wellLayer, testPoly))
return testPoly;
}
}
// special case: active contact without substrate layer looks the same as a well/substrate tap.
// Make sure we are using the correct one.
if (pn.getFunction() == PrimitiveNode.Function.CONTACT && hasActive)
{
// active contact. If Psubstrate process, make sure Ndiff contact is not being placed on top of Nwell
// for Nsubstrate process, make sure Pdiff contact is not being placed on top of Pwell
if ((pSubstrateProcess && hasNplus) || (nSubstrateProcess && hasPplus))
{
if (merge.contains(wellLayer, testPoly))
return testPoly;
}
}
if (!approximateCuts && cutsInArea != null)
{
// make sure all cuts in area are found in the node
for(PolyBase pb : cutsInArea)
{
boolean foundIt = false;
for(int i=0; i<cutsFound.size(); i++)
{
PolyBase pb2 = cutsFound.get(i);
if (DBMath.doublesClose(pb.getCenterX()/SCALEFACTOR, pb2.getCenterX()) &&
DBMath.doublesClose(pb.getCenterY()/SCALEFACTOR, pb2.getCenterY()))
{
cutsFound.remove(i);
foundIt = true;
break;
}
}
if (!foundIt) return pb;
}
if (cutsFound.size() > 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<PolyBase,CutBound> cutMap;
private List<PolyBase> justCuts;
private RTNode cutOrg;
public CutInfo()
{
cutMap = new HashMap<PolyBase,CutBound>();
justCuts = new ArrayList<PolyBase>();
cutOrg = RTNode.makeTopLevel();
}
public List<PolyBase> 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<PolyBase>
{
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;
}
public Rectangle2D 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<root.getTotal(); j++)
{
Object child = root.getChild(j);
if (root.getFlag())
{
soFar++;
if (!recursive && (soFar % 100) == 0) Job.getUserInterface().setProgressValue(soFar * 100 / totalContacts);
NodeInst ni = (NodeInst)child;
AffineTransform trans = ni.rotateOut();
Poly [] polys = tech.getShapeOfNode(ni);
for(int i=0; i<polys.length; i++)
{
Poly poly = polys[i];
Layer layer = poly.getLayer();
// make sure the geometric database is made up of proper layers
layer = geometricLayer(layer);
if (layer.getFunction().isContact()) continue;
if (layer.getFunction().isMetal()) continue;
if (layer.getFunction().isPoly()) continue;
poly.transform(trans);
Point2D [] points = poly.getPoints();
for(int k=0; k<points.length; k++)
poly.setPoint(k, scaleUp(points[k].getX()), scaleUp(points[k].getY()));
poly.roundPoints();
subtractMerge.add(layer, poly);
}
// every 500 nodes, apply the merge to the original
if ((soFar % 500) == 0)
{
merge.subtractMerge(subtractMerge);
List<Layer> allLayers = new ArrayList<Layer>();
for(Layer lay : subtractMerge.getKeySet())
allLayers.add(lay);
for(Layer lay : allLayers)
subtractMerge.deleteLayer(lay);
}
} else
{
soFar = extractContactNodes((RTNode)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<PossibleVia> findPossibleVias(Layer lay, EditingPreferences ep)
{
List<PossibleVia> possibleVias = new ArrayList<PossibleVia>();
for(Iterator<PrimitiveNode> 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 vet each contact?
// For some reason, the MOCMOS technology with MOCMOS foundry shows the "A-" nodes (A-Metal-1-Metal-2-Con)
// but these primitives are not fully in existence, and crash here
boolean bogus = false;
Technology.NodeLayer [] nLs = pNp.getNodeLayers();
for(int i=0; i<nLs.length; i++)
{
Technology.NodeLayer nLay = nLs[i];
TechPoint [] debugPoints = nLay.getPoints();
if (debugPoints == null || debugPoints.length <= 0)
{
bogus = true;
break;
}
}
if (bogus) continue;
// load the layer information
NodeInst dummyNI = NodeInst.makeDummyInstance(pNp);
Poly[] layerPolys = tech.getShapeOfNode(dummyNI);
int cutNodeLayer = -1;
int m1Layer = -1;
int m2Layer = -1;
boolean hasPolyActive = false;
List<Integer> pvLayers = new ArrayList<Integer>();
for(int i=0; i<layerPolys.length; i++)
{
// examine one layer of the primitive node
Poly poly = layerPolys[i];
Layer nLayer = poly.getLayer();
Layer.Function lFun = nLayer.getFunction();
if (lFun.isMetal())
{
if (m1Layer < 0) m1Layer = i; else
if (m2Layer < 0) m2Layer = i;
} else if (lFun.isDiff() || lFun.isPoly()) hasPolyActive = true;
// ignore well/select layers if requested
if (ignoreActiveSelectWell && fun.isContact())
{
if (lFun.isImplant() || lFun.isSubstrate() || lFun.isWell()) continue;
}
// ignore well layers if the process doesn't have them
// if (lFun == Layer.Function.WELLP && pSubstrateProcess) continue;
// if (lFun == Layer.Function.WELLN && nSubstrateProcess) continue;
boolean cutLayer = false;
if (nLayer == lay)
{
// this is the target cut layer: mark it
cutLayer = true;
} else
{
// special case for active/poly cut confusion
if (nLayer.getFunction() == lay.getFunction()) cutLayer = true;
}
if (cutLayer) cutNodeLayer = i; else
{
pvLayers.add(new Integer(i));
}
}
if (cutNodeLayer < 0) continue;
// make sure via contacts connect exactly two layers of metal, next to each other
if (!hasPolyActive && fun.isContact())
{
boolean badContact = false;
if (m1Layer < 0 || m2Layer < 0) badContact = true; else
{
int lowMetal = layerPolys[m1Layer].getLayer().getFunction().getLevel();
int highMetal = layerPolys[m2Layer].getLayer().getFunction().getLevel();
if (lowMetal > 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<nLs.length; i++)
{
Technology.NodeLayer nLay = nLs[i];
if (nLay.getLayer() == layerPolys[cutNodeLayer].getLayer())
{
pv.multicutSep1D = nLay.getMulticutSep1D();
pv.multicutSep2D = nLay.getMulticutSep2D();
pv.multicutSizeX = nLay.getMulticutSizeX();
pv.multicutSizeX = nLay.getMulticutSizeY();
}
}
pv.minWidth = scaleUp(pNp.getDefWidth());
pv.minHeight = scaleUp(pNp.getDefHeight());
int fill = 0;
double cutLX = layerPolys[cutNodeLayer].getBounds2D().getMinX();
double cutHX = layerPolys[cutNodeLayer].getBounds2D().getMaxX();
double cutLY = layerPolys[cutNodeLayer].getBounds2D().getMinY();
double cutHY = layerPolys[cutNodeLayer].getBounds2D().getMaxY();
for(Integer layIndex : pvLayers)
{
int index = layIndex.intValue();
pv.layers[fill] = geometricLayer(layerPolys[index].getLayer());
double layLX = layerPolys[index].getBounds2D().getMinX();
double layHX = layerPolys[index].getBounds2D().getMaxX();
double layLY = layerPolys[index].getBounds2D().getMinY();
double layHY = layerPolys[index].getBounds2D().getMaxY();
pv.shrinkL[fill] = scaleUp(cutLX - layLX);
pv.shrinkR[fill] = scaleUp(layHX - cutHX);
pv.shrinkT[fill] = scaleUp(layHY - cutHY);
pv.shrinkB[fill] = scaleUp(cutLY - layLY);
fill++;
}
// add this to the list of possible vias
possibleVias.add(pv);
// if this via is asymmetric, add others with different rotation
boolean hvSymmetry = true, rotSymmetry = true;
for(int i=0; i<pv.layers.length; i++)
{
if (pv.shrinkL[i] != pv.shrinkR[i] || pv.shrinkT[i] != pv.shrinkB[i])
{
rotSymmetry = false;
break;
}
if (pv.shrinkL[i] != pv.shrinkT[i] || pNp.getDefWidth() != pNp.getDefHeight())
hvSymmetry = false;
}
if (!hvSymmetry || !rotSymmetry)
{
// horizontal/vertical or rotational symmetry missing: add a 90-degree rotation
PossibleVia newPV = new PossibleVia(pv.pNp, pv.layers.length);
newPV.rotation = 90;
newPV.multicutSep1D = pv.multicutSep1D; newPV.multicutSep2D = pv.multicutSep2D;
newPV.multicutSizeX = pv.multicutSizeX; newPV.multicutSizeY = pv.multicutSizeY;
newPV.minWidth = pv.minWidth;
newPV.minHeight = pv.minHeight;
for(int i=0; i<pv.layers.length; i++)
{
newPV.layers[i] = pv.layers[i];
newPV.shrinkL[i] = pv.shrinkT[i];
newPV.shrinkR[i] = pv.shrinkB[i];
newPV.shrinkT[i] = pv.shrinkR[i];
newPV.shrinkB[i] = pv.shrinkL[i];
}
possibleVias.add(newPV);
}
if (!rotSymmetry)
{
// rotational symmetry missing: add a 180-degree and 270-degree rotation
PossibleVia newPV = new PossibleVia(pv.pNp, pv.layers.length);
newPV.rotation = 180;
newPV.multicutSep1D = pv.multicutSep1D; newPV.multicutSep2D = pv.multicutSep2D;
newPV.multicutSizeX = pv.multicutSizeX; newPV.multicutSizeY = pv.multicutSizeY;
newPV.minWidth = pv.minWidth;
newPV.minHeight = pv.minHeight;
for(int i=0; i<pv.layers.length; i++)
{
newPV.layers[i] = pv.layers[i];
newPV.shrinkL[i] = pv.shrinkR[i];
newPV.shrinkR[i] = pv.shrinkL[i];
newPV.shrinkT[i] = pv.shrinkB[i];
newPV.shrinkB[i] = pv.shrinkT[i];
}
possibleVias.add(newPV);
newPV = new PossibleVia(pv.pNp, pv.layers.length);
newPV.rotation = 270;
newPV.multicutSep1D = pv.multicutSep1D; newPV.multicutSep2D = pv.multicutSep2D;
newPV.multicutSizeX = pv.multicutSizeX; newPV.multicutSizeY = pv.multicutSizeY;
newPV.minWidth = pv.minWidth;
newPV.minHeight = pv.minHeight;
for(int i=0; i<pv.layers.length; i++)
{
newPV.layers[i] = pv.layers[i];
newPV.shrinkL[i] = pv.shrinkB[i];
newPV.shrinkR[i] = pv.shrinkT[i];
newPV.shrinkT[i] = pv.shrinkL[i];
newPV.shrinkB[i] = pv.shrinkR[i];
}
possibleVias.add(newPV);
}
}
Collections.sort(possibleVias, new ViasBySize(ep));
return possibleVias;
}
/**
* Class to sort PossibleVia objects by their size.
*/
private static class ViasBySize implements Comparator<PossibleVia>
{
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.getDefaultLambdaExtendX(ep)*2;
double sizeY = pv1.pNp.getDefaultLambdaExtendY(ep)*2;
for(int i=0; i<layers1.length; i++)
{
Technology.NodeLayer nl = layers1[i];
if (nl.getLayer().getFunction().isSubstrate()) continue;
double lowX = nl.getLeftEdge().getMultiplier() * sizeX + nl.getLeftEdge().getAdder();
double highX = nl.getRightEdge().getMultiplier() * sizeX + nl.getRightEdge().getAdder();
double lowY = nl.getBottomEdge().getMultiplier() * sizeY + nl.getBottomEdge().getAdder();
double highY = nl.getTopEdge().getMultiplier() * sizeY + nl.getTopEdge().getAdder();
area1 += (highX-lowX) * (highY-lowY);
}
double area2 = 0;
Technology.NodeLayer [] layers2 = pv2.pNp.getNodeLayers();
sizeX = pv2.pNp.getDefaultLambdaExtendX(ep)*2;
sizeY = pv2.pNp.getDefaultLambdaExtendY(ep)*2;
for(int i=0; i<layers2.length; i++)
{
Technology.NodeLayer nl = layers2[i];
if (nl.getLayer().getFunction().isSubstrate()) continue;
double lowX = nl.getLeftEdge().getMultiplier() * sizeX + nl.getLeftEdge().getAdder();
double highX = nl.getRightEdge().getMultiplier() * sizeX + nl.getRightEdge().getAdder();
double lowY = nl.getBottomEdge().getMultiplier() * sizeY + nl.getBottomEdge().getAdder();
double highY = nl.getTopEdge().getMultiplier() * sizeY + nl.getTopEdge().getAdder();
area2 += (highX-lowX) * (highY-lowY);
}
if (area1 == area2) return 0;
if (area1 < area2) return 1;
return -1;
}
}
/**
* Method to see if a via can be placed.
* @param pv the possible via.
* @param cutBox the bounding box of the contact cuts.
* @param originalMerge the original merge data.
* @param ignorePWell true to ignore p-well layers.
* @param ignoreNWell true to ignore n-well layers.
* @return null if a contact can be created.
* If contact cannot be created, returns the offending layer.
*/
private Layer doesNodeFit(PossibleVia pv, Rectangle2D cutBox, PolyMerge originalMerge,
boolean ignorePWell, boolean ignoreNWell)
{
boolean hasPplus = false;
boolean hasNplus = false;
boolean hasActive = false;
for(int i=0; i<pv.layers.length; i++)
{
Layer l = pv.layers[i];
if (ignorePWell && l.getFunction() == Layer.Function.WELLP) continue;
if (ignoreNWell && l.getFunction() == Layer.Function.WELLN) continue;
if (l.isDiffusionLayer()) hasActive = true;
if (l.getFunction() == Layer.Function.IMPLANTN) hasNplus = true;
if (l.getFunction() == Layer.Function.IMPLANTP) hasPplus = true;
double lX = cutBox.getMinX() - pv.shrinkL[i];
double hX = cutBox.getMaxX() + pv.shrinkR[i];
double lY = cutBox.getMinY() - pv.shrinkB[i];
double hY = cutBox.getMaxY() + pv.shrinkT[i];
double layerCX = (lX + hX) / 2;
double layerCY = (lY + hY) / 2;
PolyBase layerPoly = new PolyBase(layerCX, layerCY, hX-lX, hY-lY);
if (!originalMerge.contains(l, layerPoly))
{
if (DEBUGCONTACTS) System.out.println(" CONTACT FAILS ON LAYER "+l.getName()+" WHICH MUST BE "+
TextUtils.formatDouble(lX/SCALEFACTOR)+"<=X<="+TextUtils.formatDouble(hX/SCALEFACTOR)+" AND "+
TextUtils.formatDouble(lY/SCALEFACTOR)+"<=Y<="+TextUtils.formatDouble(hY/SCALEFACTOR));
return l;
}
}
// special case: some substrate contacts in some techs do not have the substrate layer (since often the original geom
// also does not have the substrate layer). To prevent these from being used as regular contacts,
// do an extra check here
PolyBase testPoly = new PolyBase(new Point2D[]{
new Point2D.Double(cutBox.getCenterX()-1, cutBox.getCenterY()-1),
new Point2D.Double(cutBox.getCenterX()-1, cutBox.getCenterY()+1),
new Point2D.Double(cutBox.getCenterX()+1, cutBox.getCenterY()+1),
new Point2D.Double(cutBox.getCenterX()+1, cutBox.getCenterY()-1)
});
testPoly.setLayer(wellLayer);
if (pv.pNp.getFunction() == PrimitiveNode.Function.SUBSTRATE) // defines a substrate contact
{
// make sure that substrate contact is not being placed on top of Well (Nwell for psubstrate process)
if (wellLayer != null)
{
if (originalMerge.contains(wellLayer, testPoly)) {
if (DEBUGCONTACTS) System.out.println(" CONTACT FAILS ON LAYER "+wellLayer.getName()+" WHICH MUST NOT BE PRESENT AT "+
TextUtils.formatDouble(cutBox.getCenterX()/SCALEFACTOR)+","+TextUtils.formatDouble(cutBox.getCenterY()/SCALEFACTOR));
return wellLayer;
}
}
}
// special case: active contact without substrate layer looks the same as a well/substrate tap.
// Make sure we are using the correct one.
if (pv.pNp.getFunction() == PrimitiveNode.Function.CONTACT && hasActive)
{
// active contact. If Psubstrate process, make sure Ndiff contact is not being placed on top of Nwell
// for Nsubstrate process, make sure Pdiff contact is not being placed on top of Pwell
if ((pSubstrateProcess && hasNplus) || (nSubstrateProcess && hasPplus))
{
if (originalMerge.contains(wellLayer, testPoly)) {
if (DEBUGCONTACTS) System.out.println(" CONTACT FAILS ON LAYER "+wellLayer.getName()+" WHICH MUST NOT BE PRESENT AT "+
TextUtils.formatDouble(cutBox.getCenterX()/SCALEFACTOR)+","+TextUtils.formatDouble(cutBox.getCenterY()/SCALEFACTOR));
return wellLayer;
}
}
}
// all layers fit: can create the node
return null;
}
/********************************************** TRANSISTOR EXTRACTION **********************************************/
private void extractTransistors(PolyMerge merge, PolyMerge originalMerge, Cell newCell)
{
// must have a poly layer
if (polyLayer == null || tempLayer1 == null) return;
// find the transistors to create
List<PrimitiveNode> pTransistors = new ArrayList<PrimitiveNode>();
List<PrimitiveNode> nTransistors = new ArrayList<PrimitiveNode>();
for(Iterator<PrimitiveNode> it = tech.getNodes(); it.hasNext(); )
{
PrimitiveNode pNp = it.next();
if (pNp.getFunction().isPTypeTransistor() || pNp.getFunction().isNTypeTransistor())
{
List<PrimitiveNode> 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<Layer> usedLayers = new TreeSet<Layer>();
Map<Layer,MutableInteger> usageFirst = new HashMap<Layer,MutableInteger>();
Technology.NodeLayer [] layersFirst = listToUse.get(0).getNodeLayers();
for(int i=0; i<layersFirst.length; i++)
{
Layer lay = geometricLayer(layersFirst[i].getLayer());
usedLayers.add(lay);
MutableInteger mi = usageFirst.get(lay);
if (mi == null) usageFirst.put(lay, mi = new MutableInteger(0));
mi.increment();
}
Map<Layer,MutableInteger> usageNew = new HashMap<Layer,MutableInteger>();
Technology.NodeLayer [] layersNew = pNp.getNodeLayers();
for(int i=0; i<layersNew.length; i++)
{
Layer lay = geometricLayer(layersNew[i].getLayer());
usedLayers.add(lay);
MutableInteger mi = usageNew.get(lay);
if (mi == null) usageNew.put(lay, mi = new MutableInteger(0));
mi.increment();
}
for(Layer lay : usedLayers)
{
MutableInteger mi1 = usageFirst.get(lay);
MutableInteger mi2 = usageNew.get(lay);
if (mi1 == null || mi2 == null || mi1.intValue() != mi2.intValue()) { same = false; break; }
}
}
*/
if (same) listToUse.add(pNp);
}
}
if (nActiveLayer == pActiveLayer)
{
for(PrimitiveNode pNp : nTransistors) pTransistors.add(pNp);
if (pTransistors.size() > 0)
findTransistors(pTransistors, pActiveLayer, merge, originalMerge, newCell);
} else
{
if (nTransistors.size() > 0)
findTransistors(nTransistors, nActiveLayer, merge, originalMerge, newCell);
if (pTransistors.size() > 0)
findTransistors(pTransistors, pActiveLayer, merge, originalMerge, newCell);
}
}
private void findTransistors(List<PrimitiveNode> transistors, Layer activeLayer, PolyMerge merge, PolyMerge originalMerge, Cell newCell)
{
originalMerge.intersectLayers(polyLayer, activeLayer, tempLayer1);
//System.out.print("CONSIDERING THESE TRANSISTORS:");
//for(PrimitiveNode pnp : transistors) System.out.print(" "+pnp.describe(false)); System.out.println();
List<PolyBase> 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 offgrid by a factor of 2, because only the edges matter for offgrid
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);
}
//System.out.println("EXAMINING TRANSISTOR AT ("+TextUtils.formatDistance(poly.getCenterX()/SCALEFACTOR)+","+
// TextUtils.formatDistance(poly.getCenterY()/SCALEFACTOR)+")");
if (transBox == null)
{
// complex polygon: extract angled or serpentine transistor
extractNonManhattanTransistor(poly, transistors.get(0), merge, originalMerge, newCell);
} else
{
List<String> errors = new ArrayList<String>();
for(PrimitiveNode transistor : transistors)
{
// figure out which way the poly runs in the desired transistor
NodeInst dni = NodeInst.makeDummyInstance(transistor);
Poly [] polys = transistor.getTechnology().getShapeOfNode(dni);
double widestPoly = 0, widestActive = 0;
for(int i=0; i<polys.length; i++)
{
Poly p = polys[i];
Rectangle2D bounds = p.getBounds2D();
if (p.getLayer().getFunction().isPoly()) widestPoly = Math.max(widestPoly, bounds.getWidth());
if (p.getLayer().getFunction().isDiff()) widestActive = Math.max(widestActive, bounds.getWidth());
}
boolean polyVertical = widestPoly < widestActive;
// found a manhattan transistor, determine orientation
Point2D left = new Point2D.Double(transBox.getMinX() - 1, transBox.getCenterY());
Point2D right = new Point2D.Double(transBox.getMaxX() + 1, transBox.getCenterY());
Point2D bottom = new Point2D.Double(transBox.getCenterX(), transBox.getMinY() - 1);
Point2D top = new Point2D.Double(transBox.getCenterX(), transBox.getMaxY() + 1);
if (polyVertical)
{
Point2D swap = left; left = top; top = right; right = bottom; bottom = swap;
}
int angle = 0;
double wid = transBox.getWidth();
double hei = transBox.getHeight();
if (originalMerge.contains(polyLayer, left) && originalMerge.contains(polyLayer, right) &&
originalMerge.contains(activeLayer, top) && originalMerge.contains(activeLayer, bottom))
{
} else if (originalMerge.contains(activeLayer, left) && originalMerge.contains(activeLayer, right) &&
originalMerge.contains(polyLayer, top) && originalMerge.contains(polyLayer, bottom))
{
angle = 900;
wid = transBox.getHeight();
hei = transBox.getWidth();
} else
{
addErrorLog(newCell, "Transistor at (" + TextUtils.formatDistance(transBox.getCenterX()/SCALEFACTOR) +
"," + TextUtils.formatDistance(transBox.getCenterY()/SCALEFACTOR) +
") doesn't have proper tabs...not extracted at ("+cX+","+cY+")");
continue;
}
SizeOffset so = transistor.getProtoSizeOffset();
double width = wid + scaleUp(so.getLowXOffset() + so.getHighXOffset());
double height = hei + scaleUp(so.getLowYOffset() + so.getHighYOffset());
// make sure all layers fit
EPoint ctr = new EPoint(cX/SCALEFACTOR, cY/SCALEFACTOR);
NodeInst ni = makeDummyNodeInst(transistor, ctr, width, height,
Orientation.fromAngle(angle), Technology.NodeLayer.MULTICUT_CENTERED);
String msg = dummyTransistorFits(ni, originalMerge, newCell);
if (msg != null) { errors.add(msg); continue; }
realizeNode(transistor, Technology.NodeLayer.MULTICUT_CENTERED, cX, cY,
width, height, angle, null, merge, newCell, null);
errors.clear();
break;
}
if (errors.size() > 0)
{
for (String msg : errors)
addErrorLog(newCell, msg, new EPoint(cX/SCALEFACTOR, cY/SCALEFACTOR));
}
}
}
}
originalMerge.deleteLayer(tempLayer1);
}
/**
* Method to ensure that a proposed transistor fits in the merge.
* @param ni the transistor (a dummy NodeInst).
* @param merge the merge to test.
* @return an error message explaining why it doesn't fit (or null if it fits)
*/
private String dummyTransistorFits(NodeInst ni, PolyMerge merge, Cell cell)
{
AffineTransform trans = ni.rotateOut();
Technology tech = ni.getProto().getTechnology();
Poly [] polys = tech.getShapeOfNode(ni);
for(Poly poly : polys)
{
Layer l = poly.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;
poly.setLayer(l);
poly.transform(trans);
Point2D[] points = poly.getPoints();
for(int i=0; i<points.length; i++)
poly.setPoint(i, scaleUp(points[i].getX()), scaleUp(points[i].getY()));
if (!merge.contains(l, poly))
{
if (poly != null)
{
Rectangle2D bounds = poly.getBounds2D();
return "Cell " + cell.describe(false) + ": " + ni.getProto().describe(false) +
" at (" + TextUtils.formatDistance(ni.getAnchorCenterX()) +
"," + TextUtils.formatDistance(ni.getAnchorCenterY()) +
"), size " + TextUtils.formatDistance(ni.getLambdaBaseXSize()) + "x" +
TextUtils.formatDistance(ni.getLambdaBaseYSize()) + ", is too large on layer " +
l.getName() + " (it runs from " +
TextUtils.formatDistance(bounds.getMinX()/SCALEFACTOR) + "<=X<=" +
TextUtils.formatDistance(bounds.getMaxX()/SCALEFACTOR) + " and " +
TextUtils.formatDistance(bounds.getMinY()/SCALEFACTOR) + "<=Y<=" +
TextUtils.formatDistance(bounds.getMaxY()/SCALEFACTOR) + ")";
}
}
}
return null;
}
/**
* Method to extract a transistor from a nonmanhattan 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)
{
// determine minimum width of polysilicon
SizeOffset so = transistor.getProtoSizeOffset();
double minWidth = transistor.getDefHeight() - so.getLowYOffset() - so.getHighYOffset();
// reduce the geometry to a skeleton of centerlines
List<Centerline> 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);
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<points.length; i++)
{
if (points[i].getX() < lX) lX = points[i].getX();
if (points[i].getX() > 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<points.length; i++)
points[i] = new EPoint((points[i].getX()) / SCALEFACTOR, (points[i].getY()) / SCALEFACTOR);
realizeNode(transistor, Technology.NodeLayer.MULTICUT_CENTERED, cX, cY, hX - lX, hY - lY, 0, points, merge, newCell, null);
}
/********************************************** CONVERT CONNECTING GEOMETRY **********************************************/
/**
* Method to look for opportunities to place arcs that connect to existing geometry.
*/
private void extendGeometry(PolyMerge merge, PolyMerge originalMerge, Cell newCell, boolean justExtend)
{
// make a list of layers that can be extended
List<Layer> extendableLayers = new ArrayList<Layer>();
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<Layer,List<PolyBase>> geomToExtend = new HashMap<Layer,List<PolyBase>>();
int soFar = 0;
for (Layer layer : extendableLayers)
{
List<PolyBase> 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);
EditingPreferences ep = newCell.getEditingPreferences();
soFar = 0;
for (Layer layer : extendableLayers)
{
ArcProto ap = arcsForLayer.get(layer);
if (ap == null) continue;
double wid = ap.getDefaultLambdaBaseWidth(ep);
double arcLayerWidth = 2*(ap.getDefaultInst(ep).getLambdaExtendOverMin() + ap.getLayerLambdaExtend(layer));
//System.out.println("CONSIDERING EXTENSION OF LAYER "+layer.getName()+" WHOSE ARCS ARE "+wid+" WIDE, LAYERS ARE "+arcLayerWidth+" WIDE");
List<PolyBase> polyList = geomToExtend.get(layer);
for(PolyBase poly : polyList)
{
soFar++;
if (!recursive) Job.getUserInterface().setProgressValue(soFar * 100 / totExtensions);
// find out what this polygon touches
Map<Network,Object> netsThatTouch = getNetsThatTouch(poly, newCell, justExtend);
if (netsThatTouch == null) continue;
// make a list of port/arc ends that touch this polygon
List<Object> objectsToConnect = new ArrayList<Object>();
for(Network net : netsThatTouch.keySet())
{
Object entry = netsThatTouch.get(net);
if (entry != null) objectsToConnect.add(entry);
}
//System.out.println(" CONSIDERING POLYGON THAT TOUCHES "+objectsToConnect.size()+" PORTS");
// if only 1 object touches the polygon, see if it can be "wired" to cover
if (objectsToConnect.size() == 1)
{
// touches just 1 object: see if that object can be extended to cover the polygon
extendObject((ElectricObject)objectsToConnect.get(0), poly, layer, ap, merge, originalMerge, newCell);
continue;
}
// if two objects touch the polygon, see if an arc can connect them
if (!justExtend && objectsToConnect.size() == 2)
{
ElectricObject obj1 = (ElectricObject)objectsToConnect.get(0);
ElectricObject obj2 = (ElectricObject)objectsToConnect.get(1);
if (obj1 instanceof ArcInst)
{
PortInst pi = findArcEnd((ArcInst)obj1, poly);
if (pi == null)
{
findArcEnd((ArcInst)obj1, poly);
continue;
}
obj1 = pi;
}
if (obj2 instanceof ArcInst)
{
PortInst pi = findArcEnd((ArcInst)obj2, poly);
if (pi == null)
{
findArcEnd((ArcInst)obj2, poly);
continue;
}
obj2 = pi;
}
PortInst pi1 = (PortInst)obj1;
PortInst pi2 = (PortInst)obj2;
// see if the ports can connect in a line
Poly poly1 = pi1.getPoly();
Poly poly2 = pi2.getPoly();
Rectangle2D polyBounds1 = poly1.getBounds2D();
Rectangle2D polyBounds2 = poly2.getBounds2D();
if (polyBounds1.getMinX() <= polyBounds2.getMaxX() && polyBounds1.getMaxX() >= polyBounds2.getMinX())
{
// vertical connection
double xpos = polyBounds1.getCenterX();
if (xpos < polyBounds2.getMinX()) xpos = polyBounds2.getMinX();
if (xpos > polyBounds2.getMaxX()) xpos = polyBounds2.getMaxX();
if (alignment != null && alignment.getWidth() > 0)
{
xpos = Math.round(xpos / scaleUp(alignment.getWidth())) * scaleUp(alignment.getWidth());
if (xpos < polyBounds2.getMinX() || xpos > polyBounds2.getMaxX()) continue;
}
Point2D pt1 = new Point2D.Double(xpos, polyBounds1.getCenterY());
Point2D pt2 = new Point2D.Double(xpos, polyBounds2.getCenterY());
MutableBoolean headExtend = new MutableBoolean(true), tailExtend = new MutableBoolean(true);
originalMerge.arcPolyFits(layer, pt1, pt2, arcLayerWidth, headExtend, tailExtend);
realizeArc(ap, pi1, pi2, pt1, pt2, wid, !headExtend.booleanValue(), !tailExtend.booleanValue(), merge);
continue;
}
if (polyBounds1.getMinY() <= polyBounds2.getMaxY() && polyBounds1.getMaxY() >= polyBounds2.getMinY())
{
// horizontal connection
double ypos = polyBounds1.getCenterY();
if (ypos < polyBounds2.getMinY()) ypos = polyBounds2.getMinY();
if (ypos > polyBounds2.getMaxY()) ypos = polyBounds2.getMaxY();
if (alignment != null && alignment.getHeight() > 0)
{
ypos = Math.round(ypos / scaleUp(alignment.getHeight())) * scaleUp(alignment.getHeight());
if (ypos < polyBounds2.getMinY() || ypos > polyBounds2.getMaxY()) continue;
}
Point2D pt1 = new Point2D.Double(polyBounds1.getCenterX(), ypos);
Point2D pt2 = new Point2D.Double(polyBounds2.getCenterX(), ypos);
MutableBoolean headExtend = new MutableBoolean(true), tailExtend = new MutableBoolean(true);
originalMerge.arcPolyFits(layer, pt1, pt2, arcLayerWidth, headExtend, tailExtend);
realizeArc(ap, pi1, pi2, pt1, pt2, wid, !headExtend.booleanValue(), !tailExtend.booleanValue(), merge);
continue;
}
// see if a bend can be made through the polygon
Point2D pt1 = new Point2D.Double(polyBounds1.getCenterX(), polyBounds1.getCenterY());
Point2D pt2 = new Point2D.Double(polyBounds2.getCenterX(), polyBounds2.getCenterY());
Point2D corner1 = new Point2D.Double(polyBounds1.getCenterX(), polyBounds2.getCenterY());
Point2D corner2 = new Point2D.Double(polyBounds2.getCenterX(), polyBounds1.getCenterY());
Point2D containsIt = null;
if (poly.contains(corner1)) containsIt = corner1; else
if (poly.contains(corner2)) containsIt = corner2;
if (containsIt != null)
{
PrimitiveNode np = ap.findPinProto();
NodeInst ni = createNode(np, containsIt, np.getDefWidth(), np.getDefHeight(), null, newCell);
PortInst pi = ni.getOnlyPortInst();
MutableBoolean headExtend = new MutableBoolean(true), tailExtend = new MutableBoolean(true);
originalMerge.arcPolyFits(layer, pt1, containsIt, arcLayerWidth, headExtend, tailExtend);
realizeArc(ap, pi1, pi, pt1, containsIt, wid, !headExtend.booleanValue(), !tailExtend.booleanValue(), merge);
headExtend.setValue(true); tailExtend.setValue(true);
originalMerge.arcPolyFits(layer, pt2, containsIt, arcLayerWidth, headExtend, tailExtend);
realizeArc(ap, pi2, pi, pt2, containsIt, wid, !headExtend.booleanValue(), !tailExtend.booleanValue(), merge);
}
}
}
}
}
/**
* Method to see if a polygon can be covered by adding an arc to an Arc or Port.
* @param obj the object that is being extended (either an ArcInst or a PortInst).
* @param poly the polygon that is being covered.
* @param layer the layer of the polygon.
* @param ap the ArcProto to use when covering the polygon.
* @param merge the merge area being replaced.
* @param originalMerge the original merge area being covered.
* @param newCell the Cell in which to place the new arc.
*/
private void extendObject(ElectricObject obj, PolyBase poly, Layer layer, ArcProto ap, PolyMerge merge, PolyMerge originalMerge, Cell newCell)
{
// can only handle rectangles now
Rectangle2D polyBounds = poly.getBox();
if (polyBounds == null)
{
Rectangle2D totalBounds = poly.getBounds2D();
if (originalMerge.contains(layer, totalBounds))
polyBounds = totalBounds;
}
if (polyBounds == null) return;
//System.out.println(" EXTENDING "+obj+" TO COVER POLYGON "+(polyBounds.getMinX()/SCALEFACTOR)+"<=X<="+
// (polyBounds.getMaxX()/SCALEFACTOR)+" AND "+(polyBounds.getMinY()/SCALEFACTOR)+"<=Y<="+(polyBounds.getMaxY()/SCALEFACTOR));
// find the port that is being extended
Point2D polyCtr = new Point2D.Double(polyBounds.getCenterX(), polyBounds.getCenterY());
if (alignment != null)
{
double x = polyCtr.getX();
double y = polyCtr.getY();
if (alignment.getWidth() > 0)
x = Math.round(x / scaleUp(alignment.getWidth())) * scaleUp(alignment.getWidth());
if (alignment.getHeight() > 0)
y = Math.round(y / scaleUp(alignment.getHeight())) * scaleUp(alignment.getHeight());
polyCtr.setLocation(x, y);
}
if (obj instanceof ArcInst)
{
ArcInst ai = (ArcInst)obj;
double headDist = polyCtr.distance(ai.getHeadLocation());
double tailDist = polyCtr.distance(ai.getTailLocation());
if (headDist < tailDist) obj = ai.getHeadPortInst(); else
obj = ai.getTailPortInst();
}
PortInst pi = (PortInst)obj;
// prepare to cover the polygon
Poly portPoly = pi.getPoly();
Rectangle2D portRect = portPoly.getBounds2D();
portRect.setRect(scaleUp(portRect.getMinX()), scaleUp(portRect.getMinY()),
scaleUp(portRect.getWidth()), scaleUp(portRect.getHeight()));
PrimitiveNode np = ap.findPinProto();
// is the port inside of the area to be covered?
if (polyCtr.getY() >= portRect.getMinY() && polyCtr.getY() <= portRect.getMaxY() &&
polyCtr.getX() >= portRect.getMinX() && polyCtr.getX() <= portRect.getMaxX())
{
//System.out.println(" PORT IS INSIDE POLYGON");
// decide whether to extend horizontally or vertically
if (polyBounds.getWidth() > polyBounds.getHeight())
{
// wider area, make horizontal arcs
double endExtension = polyBounds.getHeight() / 2;
Point2D pinPt1 = new Point2D.Double((polyBounds.getMaxX() - endExtension) / SCALEFACTOR,
polyCtr.getY() / SCALEFACTOR);
Point2D pinPt2 = new Point2D.Double((polyBounds.getMinX() + endExtension) / SCALEFACTOR,
polyCtr.getY() / SCALEFACTOR);
Point2D objPt = new Point2D.Double(portRect.getCenterX() / SCALEFACTOR, polyCtr.getY() / SCALEFACTOR);
double size = Math.min(polyBounds.getWidth(), polyBounds.getHeight()) / SCALEFACTOR;
NodeInst ni1 = createNode(np, pinPt1, size, size, null, newCell);
NodeInst ni2 = createNode(np, pinPt2, size, size, null, newCell);
realizeArc(ap, ni1.getOnlyPortInst(), pi, pinPt1, objPt,
polyBounds.getHeight() / SCALEFACTOR, false, false, merge);
realizeArc(ap, ni2.getOnlyPortInst(), pi, pinPt2, objPt,
polyBounds.getHeight() / SCALEFACTOR, false, false, merge);
} else
{
// taller area, make vertical arcs
double endExtension = polyBounds.getWidth() / 2;
Point2D pinPt1 = new Point2D.Double(polyCtr.getX() / SCALEFACTOR,
(polyBounds.getMaxY() - endExtension) / SCALEFACTOR);
Point2D pinPt2 = new Point2D.Double(polyCtr.getX() / SCALEFACTOR,
(polyBounds.getMinY() + endExtension) / SCALEFACTOR);
Point2D objPt = new Point2D.Double(polyCtr.getX() / SCALEFACTOR, portRect.getCenterY() / SCALEFACTOR);
double size = Math.min(polyBounds.getWidth(), polyBounds.getHeight()) / SCALEFACTOR;
NodeInst ni1 = createNode(np, pinPt1, size, size, null, newCell);
NodeInst ni2 = createNode(np, pinPt2, size, size, null, newCell);
realizeArc(ap, ni1.getOnlyPortInst(), pi, pinPt1, objPt,
polyBounds.getWidth() / SCALEFACTOR, false, false, merge);
realizeArc(ap, ni2.getOnlyPortInst(), pi, pinPt2, objPt,
polyBounds.getWidth() / SCALEFACTOR, false, false, merge);
}
return;
}
// can we extend vertically
if (polyCtr.getX() >= portRect.getMinX() && polyCtr.getX() <= portRect.getMaxX())
{
//System.out.println(" PORT IS OUTSIDE POLYGON: EXTEND VERTICALLY");
// going up to the poly or down?
Point2D objPt = new Point2D.Double(polyCtr.getX(), portRect.getCenterY());
Point2D objPtNormal = new Point2D.Double(polyCtr.getX() / SCALEFACTOR, portRect.getCenterY() / SCALEFACTOR);
Point2D pinPt = null;
Point2D pinPtNormal = null;
boolean endExtend = true;
double endExtension = polyBounds.getWidth() / 2;
if (polyBounds.getHeight() < polyBounds.getWidth())
{
// arc is so short that it will stick out with end extension
endExtend = false;
endExtension = 0;
}
if (polyCtr.getY() > portRect.getCenterY())
{
// going up to the poly
pinPt = new Point2D.Double(polyCtr.getX(), polyBounds.getMaxY() - endExtension);
pinPtNormal = new Point2D.Double(polyCtr.getX() / SCALEFACTOR,
(polyBounds.getMaxY() - endExtension) / SCALEFACTOR);
} else
{
// going down to the poly
pinPt = new Point2D.Double(polyCtr.getX(), polyBounds.getMinY() + endExtension);
pinPtNormal = new Point2D.Double(polyCtr.getX() / SCALEFACTOR,
(polyBounds.getMinY() + endExtension) / SCALEFACTOR);
}
MutableBoolean headExtend = new MutableBoolean(endExtend), tailExtend = new MutableBoolean(endExtend);
double wid = polyBounds.getWidth();
if (originalMerge.arcPolyFits(layer, pinPt, objPt, wid, headExtend, tailExtend))
{
double size = Math.min(polyBounds.getWidth(), polyBounds.getHeight()) / SCALEFACTOR;
NodeInst ni1 = createNode(np, pinPtNormal, size, size, null, newCell);
realizeArc(ap, ni1.getOnlyPortInst(), pi, pinPtNormal, objPtNormal, wid / SCALEFACTOR,
!headExtend.booleanValue(), !tailExtend.booleanValue(), merge);
}
return;
}
// can we extend horizontally
if (polyCtr.getY() >= portRect.getMinY() && polyCtr.getY() <= portRect.getMaxY())
{
//System.out.println(" PORT IS OUTSIDE POLYGON: EXTEND HORIZONTALLY");
// going left to the poly or right?
Point2D objPt = new Point2D.Double(portRect.getCenterX(), polyCtr.getY());
Point2D objPtNormal = new Point2D.Double(portRect.getCenterX() / SCALEFACTOR, polyCtr.getY() / SCALEFACTOR);
Point2D pinPt = null;
Point2D pinPtNormal = null;
boolean endExtend = true;
double endExtension = polyBounds.getHeight() / 2;
if (polyBounds.getWidth() < polyBounds.getHeight())
{
// arc is so short that it will stick out with end extension
endExtend = false;
endExtension = 0;
}
if (polyCtr.getX() > portRect.getCenterX())
{
// going right to the poly
pinPt = new Point2D.Double(polyBounds.getMaxX() - endExtension, polyCtr.getY());
pinPtNormal = new Point2D.Double((polyBounds.getMaxX() - endExtension) / SCALEFACTOR,
polyCtr.getY() / SCALEFACTOR);
} else
{
// going left to the poly
pinPt = new Point2D.Double(polyBounds.getMinX() + endExtension, polyCtr.getY());
pinPtNormal = new Point2D.Double((polyBounds.getMinX() + endExtension) / SCALEFACTOR,
polyCtr.getY() / SCALEFACTOR);
}
MutableBoolean headExtend = new MutableBoolean(endExtend), tailExtend = new MutableBoolean(endExtend);
double wid = polyBounds.getHeight();
if (originalMerge.arcPolyFits(layer, pinPt, objPt, wid, headExtend, tailExtend))
{
double size = Math.min(polyBounds.getWidth(), polyBounds.getHeight()) / SCALEFACTOR;
NodeInst ni1 = createNode(np, pinPtNormal, size, size, null, newCell);
realizeArc(ap, ni1.getOnlyPortInst(), pi, pinPtNormal, objPtNormal,
wid / SCALEFACTOR, !headExtend.booleanValue(), !tailExtend.booleanValue(), merge);
}
}
}
private PortInst findArcEnd(ArcInst ai, PolyBase poly)
{
// see if one end of the arc touches the poly
Point2D head = ai.getHeadLocation();
Point2D tail = ai.getTailLocation();
int ang = GenMath.figureAngle(tail, head);
int angPlus = (ang + 900) % 3600;
double width = ai.getLambdaBaseWidth() / 2;
// see if the head end touches
Point2D headButFarther = new Point2D.Double(head.getX() + width * GenMath.cos(ang), head.getY() + width * GenMath.sin(ang));
if (poly.contains(headButFarther)) return ai.getHeadPortInst();
Point2D headOneSide = new Point2D.Double(head.getX() + width * GenMath.cos(angPlus), head.getY() + width * GenMath.sin(angPlus));
if (poly.contains(headOneSide)) return ai.getHeadPortInst();
Point2D headOtherSide = new Point2D.Double(head.getX() + width * GenMath.cos(angPlus), head.getY() + width * GenMath.sin(angPlus));
if (poly.contains(headOtherSide)) return ai.getHeadPortInst();
// see if the tail end touches
Point2D tailButFarther = new Point2D.Double(tail.getX() - width * GenMath.cos(ang), tail.getY() - width * GenMath.sin(ang));
if (poly.contains(tailButFarther)) return ai.getTailPortInst();
Point2D tailOneSide = new Point2D.Double(tail.getX() - width * GenMath.cos(angPlus), tail.getY() - width * GenMath.sin(angPlus));
if (poly.contains(tailOneSide)) return ai.getTailPortInst();
Point2D tailOtherSide = new Point2D.Double(tail.getX() - width * GenMath.cos(angPlus), tail.getY() - width * GenMath.sin(angPlus));
if (poly.contains(tailOtherSide)) return ai.getTailPortInst();
return null;
}
private boolean polysTouch(PolyBase poly1, PolyBase poly2)
{
Point2D [] points1 = poly1.getPoints();
Point2D [] points2 = poly2.getPoints();
if (points1.length > points2.length)
{
Point2D [] swapPts = points1; points1 = points2; points2 = swapPts;
PolyBase swapPoly = poly1; poly1 = poly2; poly2 = swapPoly;
}
// check every vertex in poly1 to see if any are in poly2
for(int i=0; i<points1.length; i++)
if (poly2.contains(points1[i])) return true;
// check every midpoint in poly1 to see if any are in poly2
for(int i=0; i<points1.length; i++)
{
int last = i-1;
if (last < 0) last = points1.length-1;
Point2D midPoint = new Point2D.Double((points1[last].getX() + points1[i].getX()) / 2,
(points1[last].getY() + points1[i].getY()) / 2);
if (poly2.contains(midPoint)) return true;
}
return false;
}
/**
* Method to build a map of networks and objects that touch a polygon.
* @param poly the polygon to analyze
* @param newCell the cell in which to search.
* @param justExtend true to insist on just 1 net; false to allow 2 nets also.
* @return the map of networks (null if too many nets).
*/
private Map<Network,Object> getNetsThatTouch(PolyBase poly, Cell newCell, boolean justExtend)
{
// scale the polygon back
Point2D [] points = poly.getPoints();
Point2D [] newPoints = new Point2D[points.length];
for(int i=0; i<points.length; i++)
newPoints[i] = new Point2D.Double(points[i].getX() / SCALEFACTOR, points[i].getY() / SCALEFACTOR);
PolyBase newPoly = new PolyBase(newPoints);
Layer layer = poly.getLayer();
// make a map of networks that touch the polygon, and the objects on them
TreeMap<Network,Object> netsThatTouch = new TreeMap<Network,Object>();
// find nodes that touch
Rectangle2D bounds = newPoly.getBounds2D();
Point2D centerPoint = new Point2D.Double(bounds.getCenterX(), bounds.getCenterY());
Netlist nl = newCell.getNetlist();
for(Iterator<RTBounds> it = newCell.searchIterator(bounds); it.hasNext(); )
{
RTBounds geom = it.next();
if (!(geom instanceof NodeInst)) continue;
NodeInst ni = (NodeInst)geom;
if (ni.isCellInstance()) continue;
AffineTransform trans = ni.rotateOut();
Technology tech = ni.getProto().getTechnology();
Poly [] nodePolys = tech.getShapeOfNode(ni, true, true, null);
for(int i=0; i<nodePolys.length; i++)
{
Poly nodePoly = nodePolys[i];
if (geometricLayer(nodePoly.getLayer()) != layer) continue;
nodePoly.transform(trans);
if (polysTouch(nodePoly, newPoly))
{
// node touches the unconnected poly: get network information
PrimitivePort pp = (PrimitivePort)nodePoly.getPort();
if (pp == null) continue;
PortInst pi = findPortInstClosestToPoly(ni, pp, centerPoint);
Network net = nl.getNetwork(pi);
if (net != null)
{
netsThatTouch.put(net, pi);
int numNets = netsThatTouch.size();
if (numNets > 2 || (numNets > 1 && justExtend)) return null;
break;
}
}
}
}
// find arcs that touch (only include if no nodes are on the network)
for(Iterator<RTBounds> it = newCell.searchIterator(bounds); it.hasNext(); )
{
RTBounds geom = it.next();
if (!(geom instanceof ArcInst)) continue;
ArcInst ai = (ArcInst)geom;
Technology tech = ai.getProto().getTechnology();
Poly [] polys = tech.getShapeOfArc(ai);
for(int i=0; i<polys.length; i++)
{
Poly arcPoly = polys[i];
if (geometricLayer(arcPoly.getLayer()) != layer) continue;
if (polysTouch(arcPoly, newPoly))
{
Network net = nl.getNetwork(ai, 0);
if (net != null)
{
if (netsThatTouch.get(net) == null) netsThatTouch.put(net, ai);
int numNets = netsThatTouch.size();
if (numNets > 2 || (numNets > 1 && justExtend)) return null;
break;
}
}
}
}
return netsThatTouch;
}
/********************************************** MISCELLANEOUS EXTRACTION HELPERS **********************************************/
/**
* Method to find a list of centerlines that skeletonize a polygon.
* @param poly the Poly to skeletonize.
* @param layer the layer on which the polygon resides.
* @param minWidth the minimum width of geometry on the layer.
* @param merge
* @param originalMerge
* @return a List of Centerline objects that describe a single "bone" of the skeleton.
*/
private List<Centerline> findCenterlines(PolyBase poly, Layer layer, double minWidth, PolyMerge merge, PolyMerge originalMerge)
{
//System.out.println("ANALYZING LAYER "+layer.getName());
// the list of centerlines
List<Centerline> validCenterlines = new ArrayList<Centerline>();
// make a layer that describes the polygon
merge.deleteLayer(tempLayer1);
merge.addPolygon(tempLayer1, poly);
List<PolyBase> polysToAnalyze = new ArrayList<PolyBase>();
polysToAnalyze.add(poly);
for(int loop=1; ; loop++)
{
// decompose all polygons
boolean foundNew = false;
//System.out.println("====== ANALYZING "+polysToAnalyze.size()+" POLYGONS: ======");
for(PolyBase aPoly : polysToAnalyze)
{
// first make a list of all parallel wires in the polygon
aPoly.setLayer(layer);
//System.out.print("FOR POLY:"); Point2D [] points = aPoly.getPoints();
//for(int i=0; i<points.length; i++) System.out.print(" ("+points[i].getX()/SCALEFACTOR+","+points[i].getY()/SCALEFACTOR+")"); System.out.println();
List<Centerline> centerlines = gatherCenterlines(aPoly, merge, originalMerge);
if (centerlines == null)
{
merge.subtract(tempLayer1, aPoly);
continue;
}
// now pull out the relevant ones
double lastWidth = -1;
boolean lastWidthNonManhattan = false;
for(Centerline cl : centerlines)
{
if (cl.width < minWidth) continue;
if (alignment != null)
{
/*
if (cl.start.getX() == cl.end.getX())
{
// vertical line: align in X
if (alignment.getWidth() > 0)
{
double aliX = scaleUp(alignment.getWidth());
double newX = Math.round(cl.start.getX() / aliX) * aliX;
cl.width -= Math.abs(newX - cl.start.getX());
cl.width = Math.floor(cl.width / aliX) * aliX;
cl.setStart(newX, cl.start.getY());
cl.setEnd(newX, cl.end.getY());
}
} else if (cl.start.getY() == cl.end.getY())
{
// horizontal line: align in Y
if (alignment.getHeight() > 0)
{
double aliY = scaleUp(alignment.getHeight());
double newY = Math.round(cl.start.getY() / aliY) * aliY;
cl.width -= Math.abs(newY - cl.start.getY());
cl.width = Math.floor(cl.width / aliY) * aliY;
cl.setStart(cl.start.getX(), newY);
cl.setEnd(cl.end.getX(), newY);
}
}
*/
}
// make the polygon to describe the centerline
double length = cl.start.distance(cl.end);
if (length < DBMath.getEpsilon()) continue;
Poly clPoly = Poly.makeEndPointPoly(length, cl.width, cl.angle,
cl.start, 0, cl.end, 0, Poly.Type.FILLED);
// see if this centerline actually covers new area
if (!merge.intersects(tempLayer1, clPoly)) continue;
// if wider than long, this cannot be the first centerline
if (validCenterlines.size() == 0)
{
//double len = cl.start.distance(cl.end);
//if (cl.width > len) continue;
}
// for nonmanhattan centerlines, do extra work to ensure uniqueness
boolean isNonManhattan = false;
if (cl.startUnscaled.getX() != cl.endUnscaled.getX() &&
cl.startUnscaled.getY() != cl.endUnscaled.getY())
{
boolean duplicate = false;
for(Centerline oCl : validCenterlines)
{
if (cl.startUnscaled.equals(oCl.startUnscaled) && cl.endUnscaled.equals(oCl.endUnscaled))
{
duplicate = true; break;
}
if (cl.startUnscaled.equals(oCl.endUnscaled) && cl.endUnscaled.equals(oCl.startUnscaled))
{
duplicate = true; break;
}
}
if (duplicate) continue;
isNonManhattan = true;
}
// if narrower centerlines have already been added, stop now
if (lastWidth < 0)
{
lastWidth = cl.width;
lastWidthNonManhattan = isNonManhattan;
}
if (Math.abs(cl.width - lastWidth) > 1)
{
if (lastWidthNonManhattan != isNonManhattan) break;
double smallest = Math.min(cl.width, lastWidth);
if (smallest != 0 && Math.max(cl.width, lastWidth) / smallest > 1.2) break;
}
// add this to the list of valid centerlines
validCenterlines.add(cl);
merge.subtract(tempLayer1, clPoly);
foundNew = true;
}
}
if (!foundNew) break;
// now analyze the remaining geometry in the polygon
polysToAnalyze = getMergePolys(merge, tempLayer1, null);
if (polysToAnalyze == null) break;
}
merge.deleteLayer(tempLayer1);
if (DEBUGCENTERLINES)
{
System.out.println("MERGED:");
for(int i=0; i<validCenterlines.size(); i++)
System.out.println(" "+validCenterlines.get(i));
}
List<Centerline> extraCenterlines = new ArrayList<Centerline>();
// now extend centerlines so they meet
Centerline [] both = new Centerline[2];
for(int i=0; i<validCenterlines.size(); i++)
{
Centerline cl = validCenterlines.get(i);
double minCLX = Math.min(cl.start.getX(), cl.end.getX()) - cl.width;
double maxCLX = Math.max(cl.start.getX(), cl.end.getX()) + cl.width;
double minCLY = Math.min(cl.start.getY(), cl.end.getY()) - cl.width;
double maxCLY = Math.max(cl.start.getY(), cl.end.getY()) + cl.width;
for(int j=i+1; j<validCenterlines.size(); j++)
{
Centerline oCl = validCenterlines.get(j);
double minOCLX = Math.min(oCl.start.getX(), oCl.end.getX()) - oCl.width;
double maxOCLX = Math.max(oCl.start.getX(), oCl.end.getX()) + oCl.width;
double minOCLY = Math.min(oCl.start.getY(), oCl.end.getY()) - oCl.width;
double maxOCLY = Math.max(oCl.start.getY(), oCl.end.getY()) + oCl.width;
if (minOCLX > maxCLX || maxOCLX < minCLX || minOCLY > maxCLY || maxOCLY < minCLY) continue;
//System.out.println("COMPARE "+cl+" WITH "+oCl);
Point2D intersect = GenMath.intersect(cl.start, cl.angle, oCl.start, oCl.angle);
if (intersect == null) continue;
//System.out.println(" INTERSECTION AT ("+TextUtils.formatDouble(intersect.getX()/SCALEFACTOR)+","+
// TextUtils.formatDouble(intersect.getY()/SCALEFACTOR)+")");
both[0] = cl; both[1] = oCl;
for(int b=0; b<2; b++)
{
Point2D newStart = both[b].start, newEnd = both[b].end;
double distToStart = newStart.distance(intersect);
double distToEnd = newEnd.distance(intersect);
// see if intersection is deeply inside of the segment
boolean makeT = insideSegment(newStart, newEnd, intersect);
if (makeT)
{
int minDistToEnd = (int)Math.min(distToStart, distToEnd);
if (minDistToEnd <= both[b].width/2) makeT = false;
}
boolean internalIntersect = false;
Line2D lineSeg = new Line2D.Double(newStart, newEnd);
if (lineSeg.ptSegDist(intersect) == 0)
internalIntersect = true;
// if intersection is off-grid, rather than having two arcs join at this point,
// which can cause off-grid errors, make a pure layer node that is the intersection area,
// and connect to that on the edges. The important difference is a pure-layer node has
// the entire node as the port, rather than an arc pin, so the connection points can be on grid.
boolean offgrid = false;
if (alignment != null && alignment.getWidth() > 0 && (intersect.getX() % scaleUp(alignment.getWidth()) != 0)) offgrid = true;
if (alignment != null && alignment.getHeight() > 0 && (intersect.getY() % scaleUp(alignment.getHeight()) != 0)) offgrid = true;
// adjust the centerline to end at the intersection point
double extendStart = 0, extendEnd = 0, extendAltStart = 0, extendAltEnd = 0, betterExtension = 0;
Point2D altNewStart = new Point2D.Double(0,0), altNewEnd = new Point2D.Double(0,0);
if (distToStart < distToEnd)
{
betterExtension = newStart.distance(intersect);
altNewStart.setLocation(newStart); altNewEnd.setLocation(intersect);
newStart = intersect;
extendAltEnd = extendStart = both[b].width / 2;
if (offgrid && !makeT) {
if (internalIntersect) {
// adjust closer end point out of intersection area
double diffX = altNewStart.getX() - intersect.getX();
double diffY = altNewStart.getY() - intersect.getY();
newStart = new Point2D.Double(intersect.getX() - diffX, intersect.getY() - diffY);
altNewEnd.setLocation(intersect.getX() + diffX, intersect.getY() + diffY);
} else {
newStart.setLocation(altNewStart);
}
extendAltEnd = extendStart = Math.abs(betterExtension);
}
else if (!makeT && betterExtension < extendStart) {
// wire will not have ends extended, add in extra wire to account for non-end extension
Centerline newCl = new Centerline(both[b].width, altNewStart, intersect);
newCl.startHub = false;
newCl.endHub = true;
if (newCl.start.distance(newCl.end) > 0)
extraCenterlines.add(newCl);
}
} else // case: distToEnd <= distToStart
{
betterExtension = newEnd.distance(intersect);
altNewStart.setLocation(intersect); altNewEnd.setLocation(newEnd);
newEnd = intersect;
extendAltStart = extendEnd = both[b].width / 2;
if (offgrid && !makeT) {
if (internalIntersect) {
double diffX = altNewEnd.getX() - intersect.getX();
double diffY = altNewEnd.getY() - intersect.getY();
newEnd = new Point2D.Double(intersect.getX() - diffX, intersect.getY() - diffY);
altNewStart.setLocation(intersect.getX() + diffX, intersect.getY() + diffY);
} else {
newEnd.setLocation(altNewEnd);
}
extendAltStart = extendEnd = Math.abs(betterExtension);
}
else if (!makeT && betterExtension < extendEnd) {
// wire will not have ends extended, add in extra wire to account for non-end extension
Centerline newCl = new Centerline(both[b].width, intersect, altNewEnd);
newCl.startHub = true;
newCl.endHub = false;
if (newCl.start.distance(newCl.end) > 0)
extraCenterlines.add(newCl);
}
}
Poly extended = Poly.makeEndPointPoly(newStart.distance(newEnd), both[b].width, both[b].angle,
newStart, extendStart, newEnd, extendEnd, Poly.Type.FILLED);
if (!originalMerge.contains(layer, extended))
{
if (extendStart > 0) extendStart = betterExtension;
if (extendEnd > 0) extendEnd = betterExtension;
extended = Poly.makeEndPointPoly(newStart.distance(newEnd), both[b].width, both[b].angle,
newStart, extendStart, newEnd, extendEnd, Poly.Type.FILLED);
}
if (originalMerge.contains(layer, extended))
{
both[b].setStart(newStart.getX(), newStart.getY());
both[b].setEnd(newEnd.getX(), newEnd.getY());
if (extendStart != 0) both[b].startHub = true;
if (extendEnd != 0) both[b].endHub = true;
if (makeT)
{
// too much shrinkage: split the centerline
Centerline newCL = new Centerline(both[b].width, altNewStart, altNewEnd);
if (extendAltStart != 0) newCL.startHub = true;
if (extendAltEnd != 0) newCL.endHub = true;
validCenterlines.add(newCL);
continue;
}
} else {
//System.out.println("Merge does not contain arc");
}
}
}
}
validCenterlines.addAll(extraCenterlines);
if (DEBUGCENTERLINES)
{
System.out.println("FINAL: ");
for(Centerline cl : validCenterlines) System.out.println(" "+cl);
}
return validCenterlines;
}
/**
* Method to tell whether a point is inside of a line segment.
* It is assumed that the point is on the line defined by the segment.
* @param start one point on the segment.
* @param end another point on the segment.
* @param pt the point in question.
* @return true if the point in question is inside of the line segment.
*/
private boolean insideSegment(Point2D start, Point2D end, Point2D pt)
{
if (pt.getX() < Math.min(start.getX(),end.getX()) || pt.getX() > Math.max(start.getX(),end.getX()) ||
pt.getY() < Math.min(start.getY(),end.getY()) || pt.getY() > Math.max(start.getY(),end.getY()))
return false;
return true;
}
/**
* Method to gather all of the Centerlines in a polygon.
* @param poly the Poly to analyze.
* @param merge the merge at this point (with extracted geometry removed).
* @param originalMerge the original collection of geometry.
* @return a List of Centerlines in the polygon.
*/
private List<Centerline> gatherCenterlines(PolyBase poly, PolyMerge merge, PolyMerge originalMerge)
{
// first make a list of all parallel wires in the polygon
List<Centerline> centerlines = new ArrayList<Centerline>();
Point2D [] points = poly.getPoints();
if (DEBUGCENTERLINES)
{
System.out.print("POLYGON ON LAYER "+poly.getLayer().getName()+":");
for(int i=0; i<points.length; i++)
System.out.print(" "+i+"("+TextUtils.formatDouble(points[i].getX()/SCALEFACTOR)+","+
TextUtils.formatDouble(points[i].getY()/SCALEFACTOR)+")");
System.out.println();
}
// make a list of line segments that are parallel
Map<Integer,List<Integer>> linesAtAngle = new TreeMap<Integer,List<Integer>>();
for(int i=0; i<points.length; i++)
{
int lastI = i-1;
if (lastI < 0) lastI = points.length-1;
Point2D lastPt = points[lastI];
Point2D thisPt = points[i];
if (lastPt.equals(thisPt)) continue;
int angle = GenMath.figureAngle(thisPt, lastPt);
while (angle < 0) angle += 1800;
while (angle >= 1800) angle -= 1800;
Integer iAngle = new Integer(angle);
List<Integer> linesSoFar = linesAtAngle.get(iAngle);
if (linesSoFar == null)
{
linesSoFar = new ArrayList<Integer>();
linesAtAngle.put(iAngle, linesSoFar);
}
linesSoFar.add(new Integer(i));
}
// now see how many holes there are (by counting colinear segments)
int colinearSegs = 0;
for(Integer iAangle : linesAtAngle.keySet())
{
// get list of all line segments that are parallel to each other
List<Integer> linesAtThisAngle = linesAtAngle.get(iAangle);
if (linesAtThisAngle == null) continue;
for(int ai=0; ai<linesAtThisAngle.size(); ai++)
{
int i = linesAtThisAngle.get(ai).intValue();
int lastI = i-1;
if (lastI < 0) lastI = points.length-1;
Point2D lastPt = points[lastI];
Point2D thisPt = points[i];
for(int aj = ai+2; aj<linesAtThisAngle.size()-1; aj++)
{
int j = linesAtThisAngle.get(aj).intValue();
if (GenMath.isOnLine(lastPt, thisPt, points[j])) colinearSegs++;
}
}
}
// boolean tooManyHoles = colinearSegs*7 > points.length;
//System.out.println(" so have "+points.length+" points with "+colinearSegs+" colinear segments"+ (tooManyHoles?" TOO MANY":""));
// if (tooManyHoles) return null;
// preallocate
Point2D [] corners = new Point2D[4];
corners[0] = new Point2D.Double(0, 0);
corners[1] = new Point2D.Double(0, 0);
corners[2] = new Point2D.Double(0, 0);
corners[3] = new Point2D.Double(0, 0);
Point2D [] possibleStart = new Point2D[4];
Point2D [] possibleEnd = new Point2D[4];
// now scan all sets of parallel lines
for(Integer iAangle : linesAtAngle.keySet())
{
// get list of all line segments that are parallel to each other
List<Integer> linesAtThisAngle = linesAtAngle.get(iAangle);
if (linesAtThisAngle == null) continue;
int angle = iAangle.intValue();
for(int ai=0; ai<linesAtThisAngle.size(); ai++)
{
int i = linesAtThisAngle.get(ai).intValue();
int lastI = i-1;
if (lastI < 0) lastI = points.length-1;
Point2D lastPt = points[lastI];
Point2D thisPt = points[i];
for(int aj = ai+1; aj<linesAtThisAngle.size(); aj++)
{
int j = linesAtThisAngle.get(aj).intValue();
Point2D oLastPt = points[j-1];
Point2D oThisPt = points[j];
// parallel lines: find point on the center line
int perpAngle = angle + 900;
Point2D oneSide = thisPt;
Point2D otherSide = GenMath.intersect(thisPt, perpAngle, oThisPt, angle);
Point2D centerPt = new Point2D.Double((oneSide.getX()+otherSide.getX()) / 2, (oneSide.getY()+otherSide.getY()) / 2);
// now determine range along that centerline
Point2D lastPtCL = GenMath.intersect(lastPt, perpAngle, centerPt, angle);
Point2D thisPtCL = GenMath.intersect(thisPt, perpAngle, centerPt, angle);
Point2D oLastPtCL = GenMath.intersect(oLastPt, perpAngle, centerPt, angle);
Point2D oThisPtCL = GenMath.intersect(oThisPt, perpAngle, centerPt, angle);
// find the bounding box of the range lines
double minX = Math.min(Math.min(lastPtCL.getX(), thisPtCL.getX()), Math.min(oLastPtCL.getX(), oThisPtCL.getX()));
double maxX = Math.max(Math.max(lastPtCL.getX(), thisPtCL.getX()), Math.max(oLastPtCL.getX(), oThisPtCL.getX()));
double minY = Math.min(Math.min(lastPtCL.getY(), thisPtCL.getY()), Math.min(oLastPtCL.getY(), oThisPtCL.getY()));
double maxY = Math.max(Math.max(lastPtCL.getY(), thisPtCL.getY()), Math.max(oLastPtCL.getY(), oThisPtCL.getY()));
// determine an extreme point along the centerline
corners[0].setLocation(minX, minY);
corners[1].setLocation(minX, maxY);
corners[2].setLocation(maxX, maxY);
corners[3].setLocation(maxX, minY);
Point2D aCorner = null;
for(int k=0; k<4; k++)
{
if (lastPtCL.equals(corners[k])) aCorner = lastPtCL;
if (thisPtCL.equals(corners[k])) aCorner = thisPtCL;
if (oLastPtCL.equals(corners[k])) aCorner = oLastPtCL;
if (oThisPtCL.equals(corners[k])) aCorner = oThisPtCL;
}
// determine distance from the extreme corner
double lastDist = aCorner.distance(lastPtCL);
double thisDist = aCorner.distance(thisPtCL);
double oLastDist = aCorner.distance(oLastPtCL);
double oThisDist = aCorner.distance(oThisPtCL);
// make sure the ranges overlap
if (Math.min(lastDist, thisDist) >= Math.max(oLastDist, oThisDist) ||
Math.min(oLastDist, oThisDist) >= Math.max(lastDist, thisDist)) continue;
if (DEBUGCENTERLINES)
System.out.println("PARALLEL LINES ("+TextUtils.formatDouble(lastPt.getX()/SCALEFACTOR)+","+TextUtils.formatDouble(lastPt.getY()/SCALEFACTOR)+
") to ("+TextUtils.formatDouble(thisPt.getX()/SCALEFACTOR)+","+TextUtils.formatDouble(thisPt.getY()/SCALEFACTOR)+")"+
" and ("+TextUtils.formatDouble(oLastPt.getX()/SCALEFACTOR)+","+TextUtils.formatDouble(oLastPt.getY()/SCALEFACTOR)+
") to ("+TextUtils.formatDouble(oThisPt.getX()/SCALEFACTOR)+","+TextUtils.formatDouble(oThisPt.getY()/SCALEFACTOR)+")");
// find the overlap
if (lastDist > thisDist)
{
double swap = lastDist; lastDist = thisDist; thisDist = swap;
Point2D swapPt = lastPtCL; lastPtCL = thisPtCL; thisPtCL = swapPt;
}
if (oLastDist > oThisDist)
{
double swap = oLastDist; oLastDist = oThisDist; oThisDist = swap;
Point2D swapPt = oLastPtCL; oLastPtCL = oThisPtCL; oThisPtCL = swapPt;
}
Point2D start, finish, oStart, oFinish;
if (lastDist < oLastDist)
{
start = oLastPtCL;
oStart = lastPtCL;
} else
{
start = lastPtCL;
oStart = oLastPtCL;
}
if (thisDist > oThisDist)
{
finish = oThisPtCL;
oFinish = thisPtCL;
} else
{
finish = thisPtCL;
oFinish = oThisPtCL;
}
// make a list of the centerline extent possibilities
possibleStart[0] = oStart; possibleEnd[0] = oFinish;
if (start.distance(oStart) < finish.distance(oFinish))
{
possibleStart[1] = oStart; possibleEnd[1] = finish;
possibleStart[2] = start; possibleEnd[2] = oFinish;
} else
{
possibleStart[1] = start; possibleEnd[1] = oFinish;
possibleStart[2] = oStart; possibleEnd[2] = finish;
}
possibleStart[3] = start; possibleEnd[3] = finish;
// try all possible spans
double width = oneSide.distance(otherSide);
for(int p=0; p<4; p++)
{
double length = possibleStart[p].distance(possibleEnd[p]);
Poly clPoly = Poly.makeEndPointPoly(length, width, angle,
possibleStart[p], 0, possibleEnd[p], 0, Poly.Type.FILLED);
if (originalMerge.contains(poly.getLayer(), clPoly))
{
// if the width is much greater than the length, rotate the centerline 90 degrees
/*
if (width > length*2)
{
Point2D [] pts = clPoly.getPoints();
Point2D [] edgeCtrs = new Point2D[pts.length];
double bestDist = Double.MAX_VALUE;
int bestPt = -1;
for(int e=0; e<pts.length; e++)
{
Point2D last;
if (e == 0) last = pts[pts.length-1]; else last = pts[e-1];
edgeCtrs[e] = new Point2D.Double((pts[e].getX() + last.getX()) / 2,
(pts[e].getY() + last.getY()) / 2);
double dist = edgeCtrs[e].distance(possibleStart[p]);
if (dist < bestDist) { bestDist = dist; bestPt = e; }
}
width = length;
int startPt = (bestPt + 1) % pts.length;
int endPt = (bestPt + 3) % pts.length;
possibleStart[p] = edgeCtrs[startPt];
possibleEnd[p] = edgeCtrs[endPt];
length = edgeCtrs[startPt].distance(edgeCtrs[endPt]);
}
*/
// get the centerline points
double psX = possibleStart[p].getX();
double psY = possibleStart[p].getY();
double peX = possibleEnd[p].getX();
double peY = possibleEnd[p].getY();
/*
// grid-align the centerline points
double xGrid = scaleUp(alignment.getWidth());
double yGrid = scaleUp(alignment.getHeight());
if (!isOnGrid(psX, xGrid))
{
if (psX > peX) psX = Math.floor(psX / xGrid) * xGrid; else
psX = Math.ceil(psX / xGrid) * xGrid;
}
if (!isOnGrid(psY, yGrid))
{
if (psY > peY) psY = Math.floor(psY / yGrid) * yGrid; else
psY = Math.ceil(psY / yGrid) * yGrid;
}
if (!isOnGrid(peX, xGrid))
{
if (peX > psX) peX = Math.floor(peX / xGrid) * xGrid; else
peX = Math.ceil(peX / xGrid) * xGrid;
}
if (!isOnGrid(peY, yGrid))
{
if (peY > psY) peY = Math.floor(peY / yGrid) * yGrid; else
peY = Math.ceil(peY / yGrid) * yGrid;
}
*/
// create the centerline
Point2D ps = new Point2D.Double(psX, psY);
Point2D pe = new Point2D.Double(peX, peY);
Centerline newCL = new Centerline(width, ps, pe);
if (newCL.angle >= 0) {
// check for redundant centerlines, favor centerlines that extend to external geometry
boolean comparisonDone = false;
for (int ci=0; ci < centerlines.size(); ci++) {
Centerline acl = centerlines.get(ci);
// same bounds
if (acl.getBounds().equals(newCL.getBounds())) {
// check if they are perpendicular - if not, they are exactly redundant
Centerline horCL = null, verCL = null;
if (acl.start.getX() == acl.end.getX()) verCL = acl;
if (acl.start.getY() == acl.end.getY()) horCL = acl;
if (newCL.start.getX() == newCL.end.getX()) verCL = newCL;
if (newCL.start.getY() == newCL.end.getY()) horCL = newCL;
if (horCL == null || verCL == null) {
comparisonDone = true;
break; // not perpendicular
}
Rectangle2D bounds = acl.getBounds();
int favorHor = 0, favorVer = 0;
// check horizontal extensions
Rectangle2D boundsE = new Rectangle2D.Double(bounds.getX(), bounds.getY(),
bounds.getWidth()+1.0/SCALEFACTOR, bounds.getHeight());
Rectangle2D boundsW = new Rectangle2D.Double(bounds.getX()-1.0/SCALEFACTOR, bounds.getY(),
bounds.getWidth()+1.0/SCALEFACTOR, bounds.getHeight());
if (originalMerge.contains(poly.getLayer(), new PolyBase(boundsE)))
favorHor++;
if (originalMerge.contains(poly.getLayer(), new PolyBase(boundsW)))
favorHor++;
// check vertical extensions
Rectangle2D boundsN = new Rectangle2D.Double(bounds.getX(), bounds.getY(),
bounds.getWidth(), bounds.getHeight()+1.0/SCALEFACTOR);
Rectangle2D boundsS = new Rectangle2D.Double(bounds.getX(), bounds.getY()-1.0/SCALEFACTOR,
bounds.getWidth(), bounds.getHeight()+1.0/SCALEFACTOR);
if (originalMerge.contains(poly.getLayer(), new PolyBase(boundsN)))
favorVer++;
if (originalMerge.contains(poly.getLayer(), new PolyBase(boundsS)))
favorVer++;
if (favorHor > favorVer) {
if (centerlines.contains(verCL)) {
if (DEBUGCENTERLINES)
System.out.println("***REMOVE "+verCL.toString()+" WHICH IS SUBOPTIMAL");
centerlines.remove(verCL);
}
if (!centerlines.contains(horCL)) {
if (DEBUGCENTERLINES) System.out.println(" MAKE "+newCL.toString());
centerlines.add(horCL);
}
comparisonDone = true;
}
if (favorVer > favorHor) {
if (centerlines.contains(horCL)) {
if (DEBUGCENTERLINES)
System.out.println("***REMOVE "+horCL.toString()+" WHICH IS SUBOPTIMAL");
centerlines.remove(horCL);
}
if (!centerlines.contains(verCL)) {
if (DEBUGCENTERLINES) System.out.println(" MAKE "+newCL.toString());
centerlines.add(verCL);
}
comparisonDone = true;
}
break;
}
}
if (!comparisonDone) {
centerlines.add(newCL);
if (DEBUGCENTERLINES) System.out.println(" MAKE "+newCL.toString());
}
}
break;
}
}
}
}
}
// sort the parallel wires by length
Collections.sort(centerlines, new ParallelWiresByLength());
if (DEBUGCENTERLINES)
{
System.out.println("SORTED BY LENGTH:");
for(Centerline cl : centerlines)
System.out.println(" "+cl.toString());
}
// remove redundant centerlines
PolyMerge reCheck = new PolyMerge();
for(int i=0; i<centerlines.size(); i++)
{
Centerline cl = centerlines.get(i);
Poly clPoly = Poly.makeEndPointPoly(cl.start.distance(cl.end), cl.width, cl.angle,
cl.start, 0, cl.end, 0, Poly.Type.FILLED);
if (reCheck.contains(tempLayer1, clPoly))
{
if (DEBUGCENTERLINES)
System.out.println("***REMOVE "+cl.toString()+" WHICH IS REDUNDANT");
centerlines.remove(i);
i--;
continue;
}
reCheck.addPolygon(tempLayer1, clPoly);
}
// sort the parallel wires by width
Collections.sort(centerlines, new ParallelWiresByWidth());
if (DEBUGCENTERLINES)
{
System.out.println("FINALLY:");
for(Centerline cl : centerlines)
System.out.println(" "+cl.toString());
}
return centerlines;
}
/**
* Class to sort Centerline objects by their width (and within that, by their length).
*/
private static class ParallelWiresByWidth implements Comparator<Centerline>
{
public int compare(Centerline cl1, Centerline cl2)
{
if (cl1.width < cl2.width) return 1;
if (cl1.width > cl2.width) return -1;
double cll1 = cl1.start.distance(cl1.end);
double cll2 = cl2.start.distance(cl2.end);
if (cll1 > cll2) return -1;
if (cll1 < cll2) return 1;
return 0;
}
}
/**
* Class to sort Centerline objects by their length (and within that, by their width).
*/
private static class ParallelWiresByLength implements Comparator<Centerline>
{
public int compare(Centerline cl1, Centerline cl2)
{
double cll1 = cl1.start.distance(cl1.end);
double cll2 = cl2.start.distance(cl2.end);
if (cll1 > cll2) return -1;
if (cll1 < cll2) return 1;
if (cl1.width < cl2.width) return -1;
if (cl1.width > cl2.width) return 1;
return 0;
}
}
/**
* Method to scan the geometric information and convert it all to pure layer nodes in the new cell.
* When all other extractions are done, this is done to preserve any geometry that is unaccounted-for.
*/
private void convertAllGeometry(PolyMerge merge, PolyMerge originalMerge, Cell newCell)
{
MutableInteger numIgnored = new MutableInteger(0);
for (Layer layer : merge.getKeySet())
{
ArcProto ap = arcsForLayer.get(layer);
List<PolyBase> polyList = getMergePolys(merge, layer, numIgnored);
if (layer.getFunction().isSubstrate() || layer.getFunction().isImplant()) {
// just dump back in original layer, as it doesn't connect to anything anyway
polyList = new ArrayList<PolyBase>(originalMerge.getObjects(layer, false, false));
}
// implant layers may be large rectangles
/*
if (layer.getFunction().isSubstrate())
{
// make a list of rectangles that are proper approximations of polygons
List<Rectangle2D> rectangleList = new ArrayList<Rectangle2D>();
for(int i=polyList.size()-1; i >= 0; i--)
{
PolyBase poly = polyList.get(i);
Rectangle2D polyBounds = poly.getBounds2D();
if (originalMerge.contains(layer, polyBounds))
{
rectangleList.add(polyBounds);
polyList.remove(i);
}
}
// iterate over the list of rectangles and aggregate into larger rectangles
for(;;)
{
Rectangle2D aggregateBounds = null;
List<Rectangle2D> aggregatedList = new ArrayList<Rectangle2D>();
for(Rectangle2D polyBounds : rectangleList)
{
if (aggregateBounds == null) aggregateBounds = polyBounds; else
{
Rectangle2D unionBounds = new Rectangle2D.Double();
Rectangle2D.union(aggregateBounds, polyBounds, unionBounds);
if (!originalMerge.contains(layer, unionBounds)) continue;
aggregateBounds = unionBounds;
}
aggregatedList.add(polyBounds);
}
if (aggregateBounds == null) break;
// valid rectangle: cover it all with a large pure layer rectangle
PrimitiveNode pNp = layer.getPureLayerNode();
double centerX = aggregateBounds.getCenterX() / SCALEFACTOR;
double centerY = aggregateBounds.getCenterY() / SCALEFACTOR;
Point2D center = new Point2D.Double(centerX, centerY);
createNode(pNp, center, aggregateBounds.getWidth() / SCALEFACTOR,
aggregateBounds.getHeight() / SCALEFACTOR, null, newCell);
for(Rectangle2D polyBounds : aggregatedList)
rectangleList.remove(polyBounds);
}
}
*/
for(PolyBase poly : polyList)
{
if (Extract.isUsePureLayerNodes()) ap = null;
// special case: a rectangle on a routable layer: make it an arc
if (ap != null)
{
Rectangle2D polyBounds = poly.getBox();
if (polyBounds == null)
{
Rectangle2D totalBounds = poly.getBounds2D();
if (originalMerge.contains(layer, totalBounds))
polyBounds = totalBounds;
}
if (polyBounds != null)
{
double width = polyBounds.getWidth();
double height = polyBounds.getHeight();
double actualWidth = 0;
if (ENFORCEMINIMUMSIZE) actualWidth = ap.getDefaultLambdaBaseWidth() * SCALEFACTOR;
if (width >= actualWidth && height >= actualWidth)
{
PrimitiveNode np = ap.findPinProto();
Point2D end1 = null, end2 = null;
double size = 0;
if (width > height)
{
// make a horizontal arc
end1 = new Point2D.Double((polyBounds.getMinX()), polyBounds.getCenterY());
end2 = new Point2D.Double((polyBounds.getMaxX()), polyBounds.getCenterY());
size = height;
} else
{
// make a vertical arc
end1 = new Point2D.Double(polyBounds.getCenterX(), (polyBounds.getMinY()));
end2 = new Point2D.Double(polyBounds.getCenterX(), (polyBounds.getMaxY()));
size = width;
}
MutableBoolean headExtend = new MutableBoolean(false), tailExtend = new MutableBoolean(false);
if (originalMerge.arcPolyFits(layer, end1, end2, size, headExtend, tailExtend))
{
// scale everything
end1 = new Point2D.Double(end1.getX() / SCALEFACTOR, end1.getY() / SCALEFACTOR);
end2 = new Point2D.Double(end2.getX() / SCALEFACTOR, end2.getY() / SCALEFACTOR);
size = size / SCALEFACTOR;
// make arc
NodeInst ni1 = createNode(np, end1, size, size, null, newCell);
NodeInst ni2 = createNode(np, end2, size, size, null, newCell);
realizeArc(ap, ni1.getOnlyPortInst(), ni2.getOnlyPortInst(), end1, end2,
size, !headExtend.booleanValue(), !tailExtend.booleanValue(), merge);
} else {
System.out.println("Arc "+layer.getName()+" did not fit at "+polyBounds.getMinX()/SCALEFACTOR+","+polyBounds.getMinY()/SCALEFACTOR);
}
continue;
}
}
}
// just generate a pure-layer node
List<NodeInst> niList = makePureLayerNodeFromPoly(poly, newCell, originalMerge);
// make well or implant hard to select
if (poly.getLayer().getFunction().isSubstrate() || poly.getLayer().getFunction().isImplant()) {
for (NodeInst ni : niList) ni.setHardSelect();
}
if (niList == null) continue;
// connect to the rest if possible
if (ap != null) for(NodeInst ni : niList)
{
PortInst fPi = ni.getOnlyPortInst();
Poly fPortPoly = fPi.getPoly();
Rectangle2D polyBounds = poly.getBounds2D();
Rectangle2D searchBound = new Rectangle2D.Double(polyBounds.getMinX()/SCALEFACTOR, polyBounds.getMinY()/SCALEFACTOR,
polyBounds.getWidth()/SCALEFACTOR, polyBounds.getHeight()/SCALEFACTOR);
PortInst bestTPi = null;
double bestLen = Double.MAX_VALUE;
Point2D bestFLoc = null;
for(Iterator<RTBounds> it = newCell.searchIterator(searchBound); it.hasNext(); )
{
RTBounds geom = it.next();
if (!(geom instanceof NodeInst)) continue;
NodeInst oNi = (NodeInst)geom;
if (oNi == ni) continue;
if (oNi.isCellInstance()) continue;
if (oNi.getProto().getTechnology() != tech) continue;
for(Iterator<PortInst> pIt = oNi.getPortInsts(); pIt.hasNext(); )
{
PortInst tPi = pIt.next();
PortProto pp = tPi.getPortProto();
if (!pp.connectsTo(ap)) continue;
EPoint tLoc = tPi.getPoly().getCenter();
EPoint tLocScaled = new EPoint(scaleUp(tLoc.getX()), scaleUp(tLoc.getY()));
Point2D fLoc = fPortPoly.closestPoint(tLoc);
EPoint fLocScaled = new EPoint(scaleUp(fLoc.getX()), scaleUp(fLoc.getY()));
double len = scaleUp(fLoc.distance(tLoc));
int angle = GenMath.figureAngle(tLoc, fLoc);
Poly conPoly = Poly.makeEndPointPoly(len, 1, angle, fLocScaled, 0, tLocScaled, 0, Poly.Type.FILLED);
if (originalMerge.contains(layer, conPoly))
{
if (len < bestLen)
{
bestLen = len;
bestTPi = tPi;
bestFLoc = fLoc;
}
}
}
}
if (bestTPi != null)
{
Poly tPortPoly = bestTPi.getPoly();
Point2D tLoc = tPortPoly.closestPoint(tPortPoly.getCenter());
ArcInst ai = realizeArc(ap, fPi, bestTPi, bestFLoc, tLoc, 0, false, false, merge);
if (ai != null) ai.setFixedAngle(false);
} else
{
// Approximating the error with center
addErrorLog(newCell, "Unable to connect unextracted polygon",
new EPoint(searchBound.getCenterX(), searchBound.getCenterY()));
}
}
}
}
// also throw in unextracted contact cuts
for (Layer layer : allCutLayers.keySet())
{
CutInfo cInfo = allCutLayers.get(layer);
for(PolyBase poly : cInfo.getCuts())
makePureLayerNodeFromPoly(poly, newCell, originalMerge);
}
if (numIgnored.intValue() > 0)
System.out.println("WARNING: Ignored " + numIgnored.intValue() +
" tiny polygons (use Network Preferences to control tiny polygon limit)");
}
/**
* Method to add back in original routing layers as pure layer nodes
* @param oldCell the original imported cell
* @param newCell the new cell
* @param merge the remaining layers to create
* @param originalMerge the original set of layers
*/
private void addInRoutingLayers(Cell oldCell, Cell newCell, PolyMerge merge, PolyMerge originalMerge)
{
Map<Layer,List<NodeInst>> newNodes = new HashMap<Layer,List<NodeInst>>();
// create new nodes as copy of old nodes
for (Iterator<NodeInst> nit = oldCell.getNodes(); nit.hasNext(); )
{
NodeInst ni = nit.next();
NodeProto np = ni.getProto();
if (!(np instanceof PrimitiveNode)) continue;
PrimitiveNode pn = (PrimitiveNode)np;
if (pn.getFunction() == PrimitiveNode.Function.NODE)
{
Layer layer = pn.getLayerIterator().next();
Layer.Function fun = layer.getFunction();
if (fun.isPoly() || fun.isMetal() || fun.isDiff())
{
List<NodeInst> newNis = new ArrayList<NodeInst>();
// create same node in new 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 coords
for (int i=0; i<origPoints.length; i++) {
points[i] = new Point2D.Double(origPoints[i].getX()+ni.getAnchorCenterX(), origPoints[i].getY()+ni.getAnchorCenterY());
}
boolean BREAKUPTRACE = true;
PolyBase poly = new PolyBase(points);
poly.setLayer(layer);
if (BREAKUPTRACE)
{
// irregular shape: break it up with simpler polygon merging algorithm
GeometryHandler thisMerge = GeometryHandler.createGeometryHandler(GeometryHandler.GHMode.ALGO_SWEEP, 1);
thisMerge.add(layer, poly);
thisMerge.postProcess(true);
Collection<PolyBase> set = ((PolySweepMerge)thisMerge).getPolyPartition(layer);
for(PolyBase simplePoly : set)
{
PolyBase simplePolyScaledUp = scaleUpPoly(simplePoly);
pn = getActiveNodeType(layer, simplePolyScaledUp, originalMerge, pn);
layer = pn.getLayerIterator().next();Rectangle2D polyBounds = simplePoly.getBounds2D();
NodeInst newNi = NodeInst.makeInstance(pn, simplePoly.getCenter(), polyBounds.getWidth(), polyBounds.getHeight(),
newCell);
if (newNi == null) continue;
newNis.add(newNi);
}
} else {
PolyBase polyScaledUp = scaleUpPoly(poly);
pn = getActiveNodeType(layer, polyScaledUp, originalMerge, pn);
NodeInst newNi = NodeInst.makeInstance(pn, ni.getAnchorCenter(), ni.getXSize(), ni.getYSize(), newCell);
if (newNi != null) {
newNis.add(newNi);
newNi.setTrace(points);
}
}
} else {
PolyBase poly = new PolyBase(ni.getBounds());
PolyBase polyScaledUp = scaleUpPoly(poly);
pn = getActiveNodeType(layer, polyScaledUp, originalMerge, pn);
NodeInst newNi = NodeInst.makeInstance(pn, ni.getAnchorCenter(), ni.getXSize(), ni.getYSize(),
newCell, ni.getOrient(), null);
if (newNi != null)
newNis.add(newNi);
}
// substract out layers
layer = pn.getLayerIterator().next();
for (NodeInst newNi : newNis) {
Poly [] polys = tech.getShapeOfNode(newNi);
for (Poly p : polys)
{
if (p.getLayer() == layer)
removePolyFromMerge(merge, layer, p);
}
}
// add to map of created nodeinsts
List<NodeInst> list = newNodes.get(layer);
if (list == null)
{
list = new ArrayList<NodeInst>();
newNodes.put(layer, list);
}
list.addAll(newNis);
}
}
}
// now stitch new nodes together
for (Layer layer : newNodes.keySet())
{
Layer.Function fun = layer.getFunction();
ArcProto ap = Generic.tech().universal_arc;
if (fun.isPoly() || fun.isMetal() || fun.isDiff()) ap = arcsForLayer.get(layer);
List<NodeInst> nodes = newNodes.get(layer);
int i=0, j=1;
// check nodes against each other
for (i=0; i<nodes.size(); i++)
{
NodeInst ni1 = nodes.get(i);
for (j=i+1; j<nodes.size(); j++)
{
NodeInst ni2 = nodes.get(j);
if (ni1 == ni2) continue;
// see if shapes intersect. If so, connect them
Poly poly1 = tech.getShapeOfNode(ni1)[0];
Poly poly2 = tech.getShapeOfNode(ni2)[0];
List<Line2D> overlappingEdges = new ArrayList<Line2D>();
List<PolyBase> intersection = poly1.getIntersection(poly2, overlappingEdges);
Point2D connectionPoint = null;
if (intersection.size() > 0)
{
// areas intersect, use center point of first common area
PolyBase pint = intersection.get(0);
connectionPoint = pint.getCenter();
// round center point
if (alignment != null)
{
double x = connectionPoint.getX();
double y = connectionPoint.getY();
if (alignment.getWidth() > 0) x = Math.round(x/alignment.getWidth()) * alignment.getWidth();
if (alignment.getHeight() > 0) y = Math.round(y/alignment.getHeight()) * alignment.getHeight();
if (pint.contains(x, y))
connectionPoint = new Point2D.Double(x, y);
}
}
else if (overlappingEdges.size() > 0)
{
// areas do not intersect, but share common edges. Use center point of first edge
Line2D line = overlappingEdges.get(0);
double x = (line.getX1()+line.getX2())/2.0;
double y = (line.getY1()+line.getY2())/2.0;
if (alignment != null) {
double newx = x, newy = y;
if ((line.getY1() == line.getY2()) && !isOnGrid(x, alignment.getWidth())) {
newx = Math.round(x/alignment.getWidth()) * alignment.getWidth();
}
if ((line.getX1() == line.getX2()) && !isOnGrid(y, alignment.getHeight())) {
newy = Math.round(y/alignment.getHeight()) * alignment.getHeight();
}
if (line.ptSegDist(newx, newy) == 0) {
x = newx;
y = newy;
}
}
connectionPoint = new Point2D.Double(x, y);
}
if (connectionPoint != null)
{
// check on ap, we need to decide if diff arc is really diff arc or well arc
// scale up poly so units match originalMerge units
PolyBase poly1Scaled = scaleUpPoly(poly1);
if (fun.isDiff()) {
ap = findArcProtoForPoly(layer, poly1Scaled, originalMerge);
}
if (ap == null) ap = Generic.tech().universal_arc;
double width = ap.getDefaultLambdaBaseWidth();
ArcProto arcProto = ap;
if (arcProto != Generic.tech().universal_arc)
{
// scale up to check merge
Point2D connPointScaled = new Point2D.Double(scaleUp(connectionPoint.getX()), scaleUp(connectionPoint.getY()));
Poly test1 = Poly.makeEndPointPoly(0.0, scaleUp(width), 0, connPointScaled, scaleUp(width/2.0), connPointScaled,
scaleUp(width/2.0), Poly.Type.FILLED);
// if on grid and merge contains, make arc
if (isOnGrid(test1) && originalMerge.contains(layer, test1))
{
realizeArc(arcProto, ni1.getOnlyPortInst(), ni2.getOnlyPortInst(), connectionPoint,
connectionPoint, width, false, false, merge);
continue;
}
arcProto = Generic.tech().universal_arc;
width = 0;
//System.out.println("Using universal arc to connect "+ni1.describe(false)+" at "+connectionPoint+" to "+ni2.describe(false));
}
realizeArc(arcProto, ni1.getOnlyPortInst(), ni2.getOnlyPortInst(), connectionPoint,
connectionPoint, width, false, false, merge);
}
}
}
}
}
private PrimitiveNode getActiveNodeType(Layer activeLayer, PolyBase poly, PolyMerge merge, PrimitiveNode defaultNode)
{
if (unifyActive || ignoreActiveSelectWell) return defaultNode;
if (defaultNode != pDiffNode && defaultNode != nDiffNode && defaultNode != diffNode) return defaultNode;
if (activeLayer.getFunction() == Layer.Function.DIFFN) {
// make sure n-diffusion is in n-select
if (merge.contains(pSelectLayer, poly)) {
// switch it pactive
return pDiffNode;
}
}
if (activeLayer.getFunction() == Layer.Function.DIFFP) {
// make sure p-diffusion is in p-select
if (merge.contains(nSelectLayer, poly)) {
// switch it nactive
return nDiffNode;
}
}
return defaultNode;
}
private boolean isOnGrid(Poly poly)
{
if (alignment != null)
{
for (Point2D p : poly.getPoints())
{
if (alignment.getWidth() > 0)
if (!isOnGrid(p.getX(), alignment.getWidth())) return false;
if (alignment.getHeight() > 0)
if (!isOnGrid(p.getY(), alignment.getHeight())) return false;
}
}
return true;
}
/**
* Returns a new poly that is a scaled up version of the given poly
* @param poly the poly
* @return a new, scaled up poly
*/
private PolyBase scaleUpPoly(PolyBase poly) {
Point2D [] points = new Point2D.Double[poly.getPoints().length];
for(int p=0; p<points.length; p++)
points[p] = new Point2D.Double(scaleUp(poly.getPoints()[p].getX()), scaleUp(poly.getPoints()[p].getY()));
PolyBase newpoly = new PolyBase(points);
newpoly.setStyle(poly.getStyle());
return newpoly;
}
/**
* Method to convert a Poly to one or more pure-layer nodes.
* @param poly the PolyBase to convert.
* @param cell the Cell in which to place the node.
* @param originalMerge the original set of layers in the unextracted cell
* @return a List of NodeInsts that were created (null on error).
*/
private List<NodeInst> makePureLayerNodeFromPoly(PolyBase poly, Cell cell, PolyMerge originalMerge)
{
Layer layer = poly.getLayer();
// if an active layer, make sure correct N/P is used
if (unifyActive && (layer == pActiveLayer || layer == nActiveLayer))
{
Rectangle2D rect = poly.getBounds2D();
rect = new Rectangle2D.Double(rect.getMinX()/SCALEFACTOR-1, rect.getMinY()/SCALEFACTOR-1,
rect.getWidth()/SCALEFACTOR+2, rect.getHeight()/SCALEFACTOR+2);
int nType = 0, pType = 0;
for(Iterator<RTBounds> it = cell.searchIterator(rect); it.hasNext(); )
{
RTBounds geom = it.next();
if (geom instanceof NodeInst)
{
NodeInst ni = (NodeInst)geom;
if (ni.isCellInstance()) continue;
Poly [] polys = ni.getProto().getTechnology().getShapeOfNode(ni);
for(int i=0; i<polys.length; i++)
{
Layer.Function fun = polys[i].getLayer().getFunction();
if (!fun.isDiff()) continue;
if (fun == Layer.Function.DIFFP) pType++;
if (fun == Layer.Function.DIFFN) nType++;
}
} else
{
ArcInst ai = (ArcInst)geom;
Poly [] polys = ai.getProto().getTechnology().getShapeOfArc(ai);
for(int i=0; i<polys.length; i++)
{
Layer.Function fun = polys[i].getLayer().getFunction();
if (!fun.isDiff()) continue;
if (fun == Layer.Function.DIFFP) pType++;
if (fun == Layer.Function.DIFFN) nType++;
}
}
}
if (pType > nType) layer = realPActiveLayer; else
layer = realNActiveLayer;
if (layer.getPureLayerNode() == null) layer = poly.getLayer();
}
PrimitiveNode pNp = layer.getPureLayerNode();
if (pNp == null)
{
System.out.println("CANNOT FIND PURE LAYER NODE FOR LAYER " + layer.getName());
return null;
}
List<NodeInst> createdNodes = new ArrayList<NodeInst>();
if (poly.getBox() == null)
{
// irregular shape: break it up with simpler polygon merging algorithm
GeometryHandler thisMerge = GeometryHandler.createGeometryHandler(GeometryHandler.GHMode.ALGO_SWEEP, 1);
thisMerge.add(layer, poly);
thisMerge.postProcess(true);
Collection<PolyBase> set = ((PolySweepMerge)thisMerge).getPolyPartition(layer);
for(PolyBase simplePoly : set)
{
Rectangle2D polyBounds = simplePoly.getBounds2D();
NodeInst ni = makeAlignedPoly(polyBounds, layer, originalMerge, pNp, cell);
if (ni == null) continue;
createdNodes.add(ni);
}
return createdNodes;
}
Rectangle2D polyBounds = poly.getBounds2D();
NodeInst ni = makeAlignedPoly(polyBounds, layer, originalMerge, pNp, cell);
if (ni != null)
createdNodes.add(ni);
// double centerX = polyBounds.getCenterX() / SCALEFACTOR;
// double centerY = polyBounds.getCenterY() / SCALEFACTOR;
// Point2D center = new Point2D.Double(centerX, centerY);
//
// // compute any trace information if the shape is nonmanhattan
// EPoint [] newPoints = null;
//// if (poly.getBox() == null)
//// {
//// // store the trace
//// Point2D [] points = poly.getPoints();
//// newPoints = new EPoint[points.length];
//// for(int i=0; i<points.length; i++)
//// newPoints[i] = new EPoint(points[i].getX() / SCALEFACTOR, points[i].getY() / SCALEFACTOR);
//// }
// NodeInst ni = createNode(pNp, center, polyBounds.getWidth() / SCALEFACTOR,
// polyBounds.getHeight() / SCALEFACTOR, newPoints, cell);
// createdNodes.add(ni);
return createdNodes;
}
private NodeInst makeAlignedPoly(Rectangle2D polyBounds, Layer layer, PolyMerge originalMerge, PrimitiveNode pNp, Cell cell)
{
double centerX = polyBounds.getCenterX() / SCALEFACTOR;
double centerY = polyBounds.getCenterY() / SCALEFACTOR;
double width = polyBounds.getWidth() / SCALEFACTOR;
double height = polyBounds.getHeight() / SCALEFACTOR;
if (alignment != null)
{
if (alignment.getWidth() > 0)
{
// centers can be offgrid, only edges matter
double aliX = Math.round(centerX / (alignment.getWidth()/2)) * (alignment.getWidth()/2);
if (aliX != centerX)
{
double newWidth = width + Math.abs(aliX-centerX)*2;
Poly rectPoly = new Poly(scaleUp(aliX), scaleUp(centerY), scaleUp(newWidth), scaleUp(height));
if (!originalMerge.contains(layer, rectPoly))
{
if (aliX > centerX) aliX -= alignment.getWidth(); else
aliX += alignment.getWidth();
newWidth = width + Math.abs(aliX-centerX)*2;
rectPoly = new Poly(scaleUp(aliX), scaleUp(centerY), scaleUp(newWidth), scaleUp(height));
if (!originalMerge.contains(layer, rectPoly)) return null;
}
centerX = aliX;
width = newWidth;
}
}
if (alignment.getHeight() > 0)
{
// centers can be offgrid, only edges matter
double aliY = Math.round(centerY / (alignment.getHeight()/2)) * (alignment.getHeight()/2);
if (aliY != centerY)
{
double newHeight = height + Math.abs(aliY-centerY)*2;
Poly rectPoly = new Poly(scaleUp(centerX), scaleUp(aliY), scaleUp(width), scaleUp(newHeight));
if (!originalMerge.contains(layer, rectPoly))
{
if (aliY > centerY) aliY -= alignment.getHeight(); else
aliY += alignment.getHeight();
newHeight = height + Math.abs(aliY-centerY)*2;
rectPoly = new Poly(scaleUp(centerX), scaleUp(aliY), scaleUp(width), scaleUp(newHeight));
if (!originalMerge.contains(layer, rectPoly)) return null;
}
centerY = aliY;
height = newHeight;
}
}
}
Point2D center = new Point2D.Double(centerX, centerY);
NodeInst ni = createNode(pNp, center, width, height, null, cell);
return ni;
}
/**
* Method to clean-up exports.
* @param oldCell the original cell (unextracted)
* @param newCell the new cell
*/
private void cleanupExports(Cell oldCell, Cell newCell)
{
// first restore original exports (which were on pure-layer nodes and must now be placed back)
for(Export e : exportsToRestore)
{
EPoint loc = e.getPoly().getCenter();
boolean found = false;
Rectangle2D bounds = new Rectangle2D.Double(loc.getX(), loc.getY(), 0, 0);
for(Iterator<RTBounds> it = newCell.searchIterator(bounds); it.hasNext(); )
{
RTBounds geom = it.next();
if (!(geom instanceof NodeInst)) continue;
NodeInst ni = (NodeInst)geom;
for(Iterator<PortInst> pIt = ni.getPortInsts(); pIt.hasNext(); )
{
PortInst pi = pIt.next();
PortProto pp = pi.getPortProto();
if (!sameConnection(e, pp)) continue;
EPoint pLoc = pi.getPoly().getCenter();
if (loc.equals(pLoc))
{
Export.newInstance(newCell, pi, e.getName());
found = true;
break;
}
}
}
if (!found)
{
// did not reexport: create the pin and export...let it get autorouted in
PrimitiveNode pnUse = null;
for(Iterator<PrimitiveNode> it = tech.getNodes(); it.hasNext(); )
{
PrimitiveNode pn = it.next();
if (!pn.getFunction().isPin()) continue;
if (!sameConnection(e, pn.getPort(0))) continue;
pnUse = pn;
break;
}
if (pnUse == null) continue;
NodeInst ni = NodeInst.makeInstance(pnUse, loc, pnUse.getDefWidth(), pnUse.getDefHeight(), newCell);
Export.newInstance(newCell, ni.getOnlyPortInst(), e.getName());
}
}
// now handle exported pins
for(ExportedPin ep : pinsForLater)
{
for(Iterator<Export> eIt = ep.ni.getExports(); eIt.hasNext(); )
{
Export e = eIt.next();
ArcProto[] possibleCons = e.getBasePort().getConnections();
ArcProto ap = possibleCons[0];
Layer layer = ap.getLayer(0);
PortInst pi = makePort(newCell, layer, ep.location);
if (pi != null)
{
// found location of export: create export if not already done
Export found = newCell.findExport(e.getName());
if (found != null) continue;
}
// must create export stand-alone (not really a good idea, but cannot find anything to connect to it)
double sX = ep.ni.getXSize();
double sY = ep.ni.getYSize();
Point2D instanceAnchor = new Point2D.Double(0, 0);
ep.trans.transform(ep.ni.getAnchorCenter(), instanceAnchor);
NodeInst newNi = NodeInst.makeInstance(ep.ni.getProto(), instanceAnchor, sX, sY, newCell);
if (newNi == null)
{
addErrorLog(newCell, "Problem creating new instance of " + ep.ni.getProto() + " for export",
new EPoint(sX, sY));
continue;
}
PortInst newPi = newNi.findPortInstFromProto(e.getOriginalPort().getPortProto());
Export.newInstance(newCell, newPi, e.getName());
}
}
// finally rename auto-generated export names to be more sensible
for(Export e : generatedExports)
{
Cell cell = e.getParent();
Netlist nl = cell.getNetlist();
Network net = nl.getNetwork(e, 0);
String exportName = null;
for(Iterator<String> nIt = net.getExportedNames(); nIt.hasNext(); )
{
String eName = nIt.next();
if (eName.startsWith("E"))
{
boolean isTemp = false;
for(Export e2 : generatedExports)
{
if (e2.getParent() == cell && e2.getName().equals(eName)) { isTemp = true; break; }
}
if (isTemp) continue;
exportName = eName;
break;
}
}
if (exportName != null)
{
exportName = ElectricObject.uniqueObjectName(exportName, cell, PortProto.class, true, true);
e.rename(exportName);
}
}
}
/**
* Method to compare two ports to see that they connect to the same set of arcs.
* @param pp1 the first port.
* @param pp2 the second port.
* @return true if they connect to the same arcs.
*/
private boolean sameConnection(PortProto pp1, PortProto pp2)
{
ArcProto [] arcs1 = pp1.getBasePort().getConnections();
ArcProto [] arcs2 = pp2.getBasePort().getConnections();
if (arcs1 == arcs2) return true;
boolean [] found = new boolean[arcs2.length];
for(int i=0; i<arcs1.length; i++)
{
if (arcs1[i].getTechnology() == Generic.tech()) continue;
int j = 0;
for( ; j<arcs2.length; j++)
{
if (arcs1[i] == arcs2[j])
{
found[j] = true;
break;
}
}
if (j >= arcs2.length) return false;
}
for(int j=0; j<arcs2.length; j++)
{
if (found[j]) continue;
if (arcs2[j].getTechnology() != Generic.tech()) return false;
}
return true;
}
private NodeInst createNode(NodeProto np, Point2D loc, double wid, double hei, EPoint [] points, Cell cell)
{
// pins cannot be smaller than their default size
if (np.getFunction().isPin())
{
if (wid < np.getDefWidth()) wid = np.getDefWidth();
if (hei < np.getDefHeight()) hei = np.getDefHeight();
}
NodeInst ni = NodeInst.makeInstance(np, loc, wid, hei, cell);
if (ni != null && points != null)
ni.setTrace(points);
if (DEBUGSTEPS)
{
if (!np.getFunction().isPin())
{
Poly niPoly = Highlight.getNodeInstOutline(ni);
addedRectangles.add(ERectangle.fromLambda(niPoly.getBounds2D()));
}
}
return ni;
}
/**
* Method to create a node and remove its geometry from the database.
* @param pNp the node to create.
* @param cutVariation the cut spacing rule to use.
* @param centerX the new node's X location.
* @param centerY the new node's Y location.
* @param width the new node's width.
* @param height the new node's height.
* @param angle the rotation of the new node.
* @param points an array of Point2D objects that define the new node's outline (null if there is no outline).
* @param merge the geometry collection. The new node will be removed from the merge.
* @param newCell the cell in which to create the new node.
* @param realizedNodes a list of nodes to which this one will be added. If null, do not add to the list.
* If not null, add to the list but DO NOT remove the realized node's geometry from the merge.
*/
private void realizeNode(PrimitiveNode pNp, int cutVariation, double centerX, double centerY, double width, double height,
int angle, Point2D [] points, PolyMerge merge, Cell newCell, List<NodeInst> realizedNodes)
{
Orientation orient = Orientation.fromAngle(angle);
double cX = centerX / SCALEFACTOR;
double cY = centerY / SCALEFACTOR;
NodeInst ni = NodeInst.makeInstance(pNp, new Point2D.Double(cX, cY),
width / SCALEFACTOR, height / SCALEFACTOR, newCell, orient, null);
if (ni == null) return;
if (cutVariation != Technology.NodeLayer.MULTICUT_CENTERED)
ni.newVar(NodeLayer.CUT_ALIGNMENT, Integer.valueOf(cutVariation));
if (points != null)
{
ni.setTrace(points);
if (DEBUGSTEPS)
{
for(int i=1; i<points.length; i++)
{
double sx = points[i-1].getX();
double sy = points[i-1].getY();
double ex = points[i].getX();
double ey = points[i].getY();
addedLines.add(ERectangle.fromLambda(sx, sy, ex-sx, ey-sy));
}
}
} else
{
if (DEBUGSTEPS)
{
if (!pNp.getFunction().isPin())
{
Poly niPoly = Highlight.getNodeInstOutline(ni);
addedRectangles.add(ERectangle.fromLambda(niPoly.getBounds2D()));
}
}
}
if (realizedNodes != null)
{
realizedNodes.add(ni);
} else
{
// now remove the generated layers from the Merge
AffineTransform trans = ni.rotateOut();
Poly [] polys = tech.getShapeOfNode(ni);
for(int i=0; i<polys.length; i++)
{
Poly poly = polys[i];
Layer layer = poly.getLayer();
// make sure the geometric database is made up of proper layers
layer = geometricLayer(layer);
poly.transform(trans);
if (Extract.isUsePureLayerNodes() && (layer.getFunction().isPoly() || layer.getFunction().isMetal())) continue;
removePolyFromMerge(merge, layer, poly);
}
}
}
/**
* Method to create an arc and remove its geometry from the database.
* @param ap the arc to create.
* @param pi1 the first side of the arc.
* @param pi2 the second side of the arc
* @param pt1 the first connection point
* @param pt2 the second connection point
* @param width the width of the arc
* @param noHeadExtend do not extend the head
* @param noTailExtend do not extend the tail
* @param merge the merge to subtract the new arc from
* @return the arc inst created
*/
private ArcInst realizeArc(ArcProto ap, PortInst pi1, PortInst pi2, Point2D pt1, Point2D pt2, double width,
boolean noHeadExtend, boolean noTailExtend, PolyMerge merge)
{
if (alignment != null)
{
if (alignment.getWidth() > 0)
width = Math.floor(width / alignment.getWidth()) * alignment.getWidth();
}
if (width == 0) ap = Generic.tech().universal_arc;
ArcInst ai = ArcInst.makeInstanceBase(ap, width, pi1, pi2, pt1, pt2, null);
if (ai == null) return null;
// special case: wide diffusion arcs on transistors should be shortened so they don't extend to other side
if (ap.getFunction().isDiffusion() && ai.getLambdaBaseWidth() > ap.getDefaultLambdaBaseWidth())
{
if (ai.getHeadPortInst().getNodeInst().getFunction().isTransistor()) noHeadExtend = true;
if (ai.getTailPortInst().getNodeInst().getFunction().isTransistor()) noTailExtend = true;
}
if (noHeadExtend) ai.setHeadExtended(false);
if (noTailExtend) ai.setTailExtended(false);
EPoint head = ai.getHeadLocation();
EPoint tail = ai.getTailLocation();
if (head.getX() != tail.getX() && head.getY() != tail.getY())
ai.setFixedAngle(false);
// remember this arc for debugging
if (DEBUGSTEPS)
{
Poly arcPoly = ai.makeLambdaPoly(ai.getGridBaseWidth(), Poly.Type.CLOSED);
addedRectangles.add(ERectangle.fromLambda(arcPoly.getBounds2D()));
}
// now remove the generated layers from the Merge
Poly [] polys = tech.getShapeOfArc(ai);
for(int i=0; i<polys.length; i++)
{
Poly poly = polys[i];
Layer layer = poly.getLayer();
// make sure the geometric database is made up of proper layers
layer = geometricLayer(layer);
if (!Extract.isUsePureLayerNodes())
removePolyFromMerge(merge, layer, poly);
}
return ai;
}
private void removePolyFromMerge(PolyMerge merge, Layer layer, Poly poly)
{
Point2D [] points = poly.getPoints();
for(int i=0; i<points.length; i++)
poly.setPoint(i, scaleUp(points[i].getX()), scaleUp(points[i].getY()));
poly.roundPoints();
merge.subtract(layer, poly);
}
private double scaleUp(double v)
{
return DBMath.round(v) * SCALEFACTOR;
}
/**
* Method to compute the proper layer to use for a given other layer.
* Because pure-layer geometry may confuse similar layers that appear
* in Electric, all common layers are converted to a single one that will
* be unambiguous. For example, all gate-poly is converted to poly-1.
* All diffusion layers are combined into one. All layers that multiply-
* define a layer function are reduced to just one with that function.
* @param layer the layer found in the circuit.
* @return the layer to use instead.
*/
private Layer geometricLayer(Layer layer)
{
// layers from an alternate technology are accepted as-is
if (layer.getTechnology() != tech) return layer;
Layer.Function fun = layer.getFunction();
// convert gate to poly1
if (fun == Layer.Function.GATE)
{
Layer polyLayer = layerForFunction.get(Layer.Function.POLY1);
if (polyLayer != null) return polyLayer;
}
// all active is one layer
if (unifyActive)
{
if (fun == Layer.Function.DIFFP || fun == Layer.Function.DIFFN)
fun = Layer.Function.DIFF;
}
// ensure the first one for the given function
Layer properLayer = layerForFunction.get(fun);
if (properLayer != null) return properLayer;
return layer;
}
private static final double CLOSEDIST = SCALEFACTOR/50;
private List<PolyBase> getMergePolys(PolyMerge merge, Layer layer, MutableInteger numIgnored)
{
List<PolyBase> polyList = merge.getMergedPoints(layer, true);
if (polyList == null) return polyList;
List<PolyBase> properPolyList = new ArrayList<PolyBase>();
for(PolyBase poly : polyList)
{
// reduce common points
Point2D [] origPoints = poly.getPoints();
Point2D [] points = new Point2D[origPoints.length];
int len = origPoints.length;
for (int i=0; i<len; i++) points[i] = origPoints[i];
for (int i=1; i<len; i++)
{
if (points[i].distance(points[i-1]) < CLOSEDIST)
{
for(int j=i; j<len; j++) points[j-1] = points[j];
len--;
i--;
}
}
if (len > 1 && points[0].distance(points[len-1]) < CLOSEDIST) len--;
// ignore polygons with no size
if (len <= 2) continue;
// anything smaller than the minimum number of grid units is ignored
double area = poly.getArea();
if (area < smallestPoly)
{
if (numIgnored != null) numIgnored.increment();
continue;
}
properPolyList.add(poly);
}
return properPolyList;
}
/**
* Class for showing progress of extraction in a modeless dialog
*/
private static class ShowExtraction extends EDialog
{
private List<List<ERectangle>> addedBatchRectangles;
private List<List<ERectangle>> addedBatchLines;
private List<String> addedBatchNames;
private int batchPosition;
private JLabel comingUp;
private ShowExtraction(Frame parent, List<List<ERectangle>> addedBatchRectangles,
List<List<ERectangle>> addedBatchLines, List<String> addedBatchNames)
{
super(parent, false);
this.addedBatchRectangles = addedBatchRectangles;
this.addedBatchLines = addedBatchLines;
this.addedBatchNames = addedBatchNames;
getContentPane().setLayout(new GridBagLayout());
setTitle("Extraction Progress");
setName("");
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent evt) { closeDialog(); }
});
GridBagConstraints gbc;
comingUp = new JLabel("Next step:");
gbc = new GridBagConstraints();
gbc.gridx = 0; gbc.gridy = 0;
gbc.gridwidth = 2;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(4, 4, 4, 4);
getContentPane().add(comingUp, gbc);
JButton prev = new JButton("Prev");
gbc = new GridBagConstraints();
gbc.gridx = 0; gbc.gridy = 1;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(4, 4, 4, 4);
prev.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { advanceDisplay(false); }
});
getContentPane().add(prev, gbc);
JButton next = new JButton("Next");
gbc = new GridBagConstraints();
gbc.gridx = 1; gbc.gridy = 1;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(4, 4, 4, 4);
next.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent evt) { advanceDisplay(true); }
});
getContentPane().add(next, gbc);
batchPosition = -1;
advanceDisplay(true);
getRootPane().setDefaultButton(next);
finishInitialization();
}
protected void escapePressed() { closeDialog(); }
private void advanceDisplay(boolean forward)
{
if (forward)
{
batchPosition++;
if (batchPosition >= addedBatchNames.size()) batchPosition = addedBatchNames.size()-1;
} else
{
batchPosition--;
if (batchPosition < 0) batchPosition = 0;
}
comingUp.setText("Batch " + (batchPosition+1) + ": " + addedBatchNames.get(batchPosition));
pack();
UserInterface ui = Job.getUserInterface();
EditWindow_ wnd = ui.getCurrentEditWindow_();
wnd.clearHighlighting();
Cell cell = wnd.getCell();
// highlight
List<ERectangle> rects = addedBatchRectangles.get(batchPosition);
for(ERectangle er : rects)
wnd.addHighlightArea(er, cell);
List<ERectangle> lines = addedBatchLines.get(batchPosition);
for(ERectangle er : lines)
wnd.addHighlightLine(new Point2D.Double(er.getMinX(), er.getMinY()),
new Point2D.Double(er.getMaxX(), er.getMaxY()), cell, false, false);
wnd.finishedHighlighting();
}
}
}