/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: StdCellParams.java
*
* Copyright (c) 2003 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.generator.layout;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import com.sun.electric.database.geometry.DBMath;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Export;
import com.sun.electric.database.hierarchy.Library;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.prototype.PortCharacteristic;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.PortInst;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.Layer;
import com.sun.electric.technology.SizeOffset;
import com.sun.electric.technology.Technology;
import com.sun.electric.tool.ncc.Ncc;
import com.sun.electric.tool.ncc.NccOptions;
import com.sun.electric.tool.ncc.result.NccResults;
import com.sun.electric.tool.Job;
/** The bottom of the PMOS well and the top of the NMOS well are at
* y=0. PMOS tracks are numbered from 1 to nbPmosTracks(). These
* numbers correspond to the lowest and highest, respectively,
* unblocked tracks in the PMOS region. NMOS tracks are numbered from
* -1 to -nbNmosTracks(). These numbers correspond to the highest and
* lowest, respectively, unblocked tracks in the NMOS region. */
public class StdCellParams {
// ---------------------- private classes --------------------------------
private static class TrackBlockages {
private static class Blockage {
double bot, top;
Blockage(double center, double width) {
bot = center - width / 2;
top = center + width / 2;
}
}
private ArrayList<Blockage> blockages = new ArrayList<Blockage>();
private double space;
TrackBlockages(double s) {
this.space = s;
}
void addBlockage(double center, double width) {
blockages.add(new Blockage(center, width));
}
boolean isBlocked(double center, double width) {
double top = center + width / 2 + space;
double bot = center + width / 2 - space;
for (int i = 0; i < blockages.size(); i++) {
Blockage b = blockages.get(i);
if (b.bot < top && b.top > bot)
return true;
}
return false;
}
}
private static void error(boolean pred, String msg) {
Job.error(pred, msg);
}
// -------------------------- private data ---------------------------------
private static final double DEF_SIZE = LayoutLib.DEF_SIZE;
private static final Variable.Key ATTR_X = Variable.newKey("ATTR_X");
private static final Variable.Key ATTR_S = Variable.newKey("ATTR_S");
private static final Variable.Key ATTR_SN = Variable.newKey("ATTR_SN");
private static final Variable.Key ATTR_SP = Variable.newKey("ATTR_SP");
// ===============================================================
/* These variable's initial values are Technology dependent */
private double nmosWellHeight;
private double pmosWellHeight;
private double gndY;
private double vddY;
private double gndWidth;
private double vddWidth;
private double trackPitch;
private double trackWidth;
private double m1TrackWidth;
private double m2TrackWidth;
private double metalSpace;
private double pmosTrackOffset;
private double nmosTrackOffset;
// An "enable" style Nand gate has a weak pullup. This is how much
// weaker than normal the PMOS is.
private double enableGateStrengthRatio;
// Separate well ties from power and ground to allow threshold
// control via substrate bias.
private boolean separateWellTies;
// maximum distance from well tie to any point in well
private double maxWellTieRadius;
// private String pmosWellTieName = "vnw";
// private String nmosWellTieName = "vsb";
private double nmosWellTieY, pmosWellTieY;
private double minGateWid;
private double maxMosWidth;
private double gridResolution;
private double drcRingSpace;
// for well contacts
private double wellConSelectHeight; // height of select layer around well tap
private double wellConSelectOffset; // offset of select from anchor point
// ======================================================
// Critera for reducing the number of distict sizes.
// Default to 10% error.
private double sizeErr = 0.1;
private static final double selectOverhangsDiffCont = 4.5;
private static final double m1OverhangsDiffCont = 2;
private static final double m1Space = 3;
private ArrayList<Double> nmosTracks = new ArrayList<Double>();
private ArrayList<Double> pmosTracks = new ArrayList<Double>();
// private int botNmosTrack, topNmosTrack, botPmosTrack, topPmosTrack;
private Library schemLib = null;
private Library layoutLib = null;
private boolean doubleStrapGate = false;
private boolean exhaustivePlace = true;
private int nbPlacerPerms = 40000;
private boolean simpleName = false;
private String vddExportName = "vdd";
private String gndExportName = "gnd";
private PortCharacteristic vddExportRole = PortCharacteristic.PWR;
private PortCharacteristic gndExportRole = PortCharacteristic.GND;
private TechType tech;
// ------------------------ private methods -----------------------------
private void initMoCMOS() {
nmosWellHeight = 70;
pmosWellHeight = 70;
gndY = -21.0;
vddY = 21.0;
gndWidth = 10;
vddWidth = 10;
trackPitch = 7;
trackWidth = 4;
m1TrackWidth = 4;
m2TrackWidth = 4;
metalSpace = 3;
pmosTrackOffset = trackPitch / 2;
nmosTrackOffset = -trackPitch / 2;
// An "enable" style Nand gate has a weak pullup. This is how much
// weaker than normal the PMOS is.
enableGateStrengthRatio = .1;
// Separate well ties from power and ground to allow threshold
// control via substrate bias.
separateWellTies = false;
// maximum distance from well tie to any point in well
maxWellTieRadius = 300;
// pmosWellTieName = "vnw";
// nmosWellTieName = "vsb";
nmosWellTieY = 0;
pmosWellTieY = 0;
minGateWid = 3;
maxMosWidth = 45;
gridResolution = 0.5;
drcRingSpace = 3;
wellConSelectHeight = 9;
wellConSelectOffset = 0;
}
private void initCMOS90() {
nmosWellHeight = 84;
pmosWellHeight = 84;
gndY = -24.5;
vddY = 24.5;
gndWidth = 9;
vddWidth = 9;
trackPitch = 7;
trackWidth = 3.4;
m1TrackWidth = 3.4;
m2TrackWidth = 2.8;
metalSpace = 2.4;
pmosTrackOffset = 7;
nmosTrackOffset = -77;
// An "enable" style Nand gate has a weak pullup. This is how much
// weaker than normal the PMOS is.
enableGateStrengthRatio = .1;
// Separate well ties from power and ground to allow threshold
// control via substrate bias.
separateWellTies = false;
// maximum distance from well tie to any point in well
maxWellTieRadius = 300;
// pmosWellTieName = "vnw";
// nmosWellTieName = "vsb";
nmosWellTieY = 0;
pmosWellTieY = 0;
minGateWid = 5.2;
maxMosWidth = 45;
gridResolution = 0.1;
drcRingSpace = 0;
wellConSelectHeight = 8.4;
wellConSelectOffset = 0;
}
/** Initialize Tracks after setting up parameters */
private void init() {
TrackBlockages blockages = new TrackBlockages(metalSpace);
// The symmetric nand2 and nand3 reserve 2 metal-2 tracks for
// internal use only to connect the output.
blockages.addBlockage(11, 4);
blockages.addBlockage(-11, 4);
// Power and ground also block metal-2 tracks
blockages.addBlockage(vddY, vddWidth);
blockages.addBlockage(gndY, gndWidth);
generateTracks(blockages);
nmosWellTieY = gndY;
pmosWellTieY = vddY;
if (separateWellTies) {
// Place well ties close to top and bottom of cells
nmosWellTieY = getTrackY(- (nbNmosTracks() - 1));
blockages.addBlockage(nmosWellTieY, trackWidth);
pmosWellTieY = getTrackY(nbPmosTracks() - 1);
blockages.addBlockage(pmosWellTieY, trackWidth);
// recompute available tracks with new blockages
generateTracks(blockages);
}
}
private boolean is90nm() {
return tech.getEnum()==TechType.TechTypeEnum.CMOS90;
}
// create a list of tracks that don't overlap vdd or gnd
private void generateTracks(TrackBlockages blockages) {
// this may be called multiple times if setSeparateWellTies is called
// 0 is an illegal index
nmosTracks.clear();
pmosTracks.clear();
// tracks in PMOS region
for (double y = pmosTrackOffset; y < pmosWellHeight; y += trackPitch) {
if (!blockages.isBlocked(y, trackWidth))
pmosTracks.add(new Double(y));
}
// tracks in NMOS region
for (double y = nmosTrackOffset;
y > -nmosWellHeight;
y -= trackPitch) {
if (!blockages.isBlocked(y, trackWidth))
nmosTracks.add(new Double(y));
}
}
private double quantizeMantissa(double mantissa) {
double szRatio = (1 + sizeErr) / (1 - sizeErr);
// find n such that (szRatio^n) is closest to mantissa
double logBaseSzRatio = Math.log(mantissa) / Math.log(szRatio);
double floorN = Math.floor(logBaseSzRatio);
double ceilN = Math.ceil(logBaseSzRatio);
double hiApprox = Math.pow(szRatio, ceilN);
double loApprox = Math.pow(szRatio, floorN);
return (hiApprox - mantissa < mantissa - loApprox)
? (hiApprox)
: (loApprox);
}
private int calcNbGroups(double maxAvailWid, double totWid, int groupSz) {
int nbGroups = (int) Math.ceil(totWid / maxAvailWid / groupSz);
// If groupSz is even then we always create an even number of
// fingers and we don't have to add more fingers to reduce
// diffusion capacitance.
if (groupSz % 2 == 0)
return nbGroups;
// If nbGroups is even then we always create an even number of
// fingers and we don't have to add more fingers to reduce
// diffusion capacitance
if (nbGroups % 2 == 0)
return nbGroups;
// if totWid is less than maxAvailWid and groupSz is 1, just
// use 1 gate (no fingers)
if ((totWid < maxAvailWid) && (groupSz == 1))
return 1;
// try adding one more group to get an even number of fingers
int roundupGroups = nbGroups + 1;
double wid = totWid / groupSz / roundupGroups;
// Don't fold if gate width is less than width of diffusion
// contact.
if (wid >= tech.getDiffContWidth())
nbGroups = roundupGroups;
return nbGroups;
}
private void fillDiffAndSelectNotch(PortInst prevPort, PortInst thisPort,
FoldedMos mosLeft, FoldedMos mosRight, boolean fillDiffNotch) {
double diffWid = mosLeft.getPhysWidth();
Cell f = mosLeft.getSrcDrn(0).getNodeInst().getParent();
// PrimitiveNode diffCont =
// mosLeft instanceof FoldedPmos ? tech.pdm1 : tech.ndm1;
ArcProto diffArc = mosLeft instanceof FoldedPmos ? tech.pdiff() : tech.ndiff();
NodeProto diffNode =
mosLeft instanceof FoldedPmos ? tech.pdNode() : tech.ndNode();
double prevX = LayoutLib.roundCenterX(prevPort);
double thisX = LayoutLib.roundCenterX(thisPort);
double dist = thisX - prevX;
// If they overlap perfectly or if they're so far apart there's no
// select notch then no notches of any kind are possible.
if (dist == 0 || (dist >= selectOverhangsDiffCont * 2 + tech.getSelectSpacingRule()))
return;
// Fill a notch with diffusion.
//
// I can't fill notches using a diffusion arc because the span in
// the Y coordinate of the port of the diffusion contact may not
// include the center of such an arc. This occurs for MOS
// transistors with a single contact cut.
//
// I therefore fill the notch using a diffusion node.
double mosY = mosLeft.getMosCenterY();
double mosRightY = mosRight.getMosCenterY();
// if they are not aligned along Y, the extra select node might not cover top transistor.
if (!DBMath.areEquals(mosY, mosRightY))
{
double activePlusSelect = diffWid/2 + tech.getSelectSurroundDiffInTrans();
double topY = -activePlusSelect, bottomY = activePlusSelect;
double sign = 1;
if (mosY > mosRightY)
{
topY += mosY; bottomY += mosRightY; sign = -1;
}
else
{
topY += mosRightY; bottomY += mosY;
}
double diff = DBMath.round(topY - bottomY);
if (DBMath.isGreaterThan(diff, 0))
{
// round diff to multiple of min precision otherwise diff/2 (or node center will generate DRC errors)
// double minRe = f.getTechnology().getResolution()*2;
double minRe = getGridResolution()*2;
if (minRe > 0 && DBMath.hasRemainder(diff, minRe))
{
diff += getGridResolution(); // to ceil correctlu otherwise it could leave a notch
diff = ((int)(Math.ceil(diff/minRe))) * minRe;
}
diffWid += diff;
mosY += DBMath.round(sign * diff/2);
}
}
double a = mosLeft.getDiffContWidth();
double b = mosLeft.getGateWidth();
Rectangle2D diffNodeBnd = LayoutLib.calculateNodeInst(diffNode, thisX-dist/2, mosY,
dist, diffWid);
if (fillDiffNotch)
{
NodeInst dFill = LayoutLib.newNodeInst(diffNode, diffNodeBnd, 0, f);
double contY = LayoutLib.roundCenterY(thisPort); // contact is always on grid
LayoutLib.newArcInst(diffArc, DEF_SIZE, thisPort, thisX, contY,
dFill.getOnlyPortInst(),
LayoutLib.roundCenterX(dFill.getOnlyPortInst()),
contY);
// Never place wide arcs directly onto the FoldedMos
// diffusion-metal1 contacts because they become bad width hints.
if (dist < m1OverhangsDiffCont * 2 + m1Space) {
// m1 is 1 lambda narrower than diffusion contact width
double m1Wid = mosLeft.getDiffContWidth() - 1;
NodeInst mFill =
LayoutLib.newNodeInst(tech.m1Node(), thisX-dist/2, contY, dist,
m1Wid, 0, f);
LayoutLib.newArcInst(tech.m1(), DEF_SIZE, thisPort, mFill.getOnlyPortInst());
}
}
// Rounding values to avoid out-of-grid values
double roundedPosY = DBMath.round(diffNodeBnd.getY() - diffNodeBnd.getHeight()/2);
Rectangle2D selectRec = new Rectangle2D.Double(diffNodeBnd.getX() - dist/2, roundedPosY,
diffNodeBnd.getWidth(), diffNodeBnd.getHeight());
double selectDiff = (a - b);
// if (DBMath.isGreaterThan(selectDiff, 0))
addSelAroundDiff(diffNode, selectRec, selectDiff, f);
}
private static FoldedMos getRightMos(Object a) {
if (a instanceof FoldedMos) return (FoldedMos) a;
error(!(a instanceof FoldedMos[]), "not FoldedMos or FoldedMos[]");
FoldedMos[] moss = (FoldedMos[]) a;
return moss[moss.length - 1];
}
private static String trkMsg(Object key, Cell schem) {
return "Track assignment for export: "
+ key
+ " in Cell: "
+ schem.getName()
+ ".\n ";
}
//----------------------------- public constants --------------------------
/** This class allows the user to specify which source/drains
* wireVddGnd() should connect to power or ground. Most users will
* be happy with the predefined instances: ODD or EVEN. */
public interface SelectSrcDrn {
public boolean connectThisOne(int mosNdx, int srcDrnNdx);
}
public static final SelectSrcDrn EVEN = new SelectSrcDrn() {
public boolean connectThisOne(int mosNdx, int srcDrnNdx) {
return srcDrnNdx % 2 == 0;
}
};
public static final SelectSrcDrn ODD = new SelectSrcDrn() {
public boolean connectThisOne(int mosNdx, int srcDrnNdx) {
return srcDrnNdx % 2 == 1;
}
};
//------------------------ public methods --------------------------------------
//------------------------------------------------------------------------------
// Allow user to customize standard cell characteristics
//
/** Attach the well ties to special busses rather than to Vdd and Gnd. */
public void setDoubleStrapGate(boolean val) {
doubleStrapGate = val;
}
/** GasP generators use exhaustive search for gate placement */
public void setExhaustivePlace(boolean val) {
exhaustivePlace = val;
}
/** Set number of permuations GasP placer should try before giving
* up. */
public void setNbPlacerPerms(int i) {
nbPlacerPerms = i;
}
/** Units of lambda */
public void setNmosWellHeight(double h) {
nmosWellHeight = h;
init();
}
/** Units of lambda */
public void setPmosWellHeight(double h) {
pmosWellHeight = h;
init();
}
/** Set the maximum width of a each MOS finger. Units of lambda */
public void setMaxMosWidth(double wid) {
maxMosWidth = wid;
}
/** Set the starting offset from 0 of the pmos tracks */
public void setPmosTrackOffset(double offset) {
pmosTrackOffset = offset;
init();
}
/** Set the starting offset from 0 of the nmos tracks */
public void setNmosTrackOffset(double offset) {
nmosTrackOffset = offset;
init();
}
/** Set the track width */
public void setTrackWidth(double width) {
trackWidth = width;
init();
}
/** Set the width of the Vdd track */
public void setVddWidth(double width) {
vddWidth = width;
init();
}
/** Set the width of the Gnd track */
public void setGndWidth(double width) {
gndWidth = width;
init();
}
public void setM1TrackWidth(double width) {
m1TrackWidth = width;
}
public double getM1TrackWidth() { return m1TrackWidth; }
public void setM2TrackWidth(double width) {
m2TrackWidth = width;
}
public double getM2TrackWidth() { return m2TrackWidth; }
public double getDRCRingSpacing() { return drcRingSpace; }
public double getM1TrackAboveVDD() {
double trackX;
if (is90nm()) {
// vddTop + m1_m1_sp + m1_wid/2 + m1_xcon_extension
trackX = getVddY() + getVddWidth()/2 + 2.4 + getM1TrackWidth()/2 + 2.8;
} else {
// vddTop + m1_m1_sp + m1_wid/2
trackX = getVddY() + getVddWidth()/2 + 3 + 2;
}
return trackX;
}
public double getM1TrackBelowGnd() {
double trackX;
double gndBot = getGndY() - getGndWidth()/2;
if (is90nm()) {
// gndBot - m1_m1_sp - m1_wid/2 - m1_xcon_extension
trackX = gndBot - 2.4 - getM1TrackWidth()/2 - 2.8;
} else {
// gndBot - m1_m1_sp - m1_wid/2
trackX = gndBot - getM1TrackWidth() - 2;
}
return trackX;
}
public double getWellOverhangDiff() {
if (is90nm()) return 8;
else return 6;
}
public double getGridResolution() { return gridResolution; }
/** Get the fold pitch for folded transistor, given the number of series transistors */
public double getFoldPitch(int nbSeries) {
if (is90nm())
return (8 + (nbSeries - 1) * (3 + 2));
else
return (8 + (nbSeries - 1) * (3 + 2));
}
/** Turn on Network Consistency Checking after each gate is generated.
*<p> This just checks topology and ignores sizes. */
public void enableNCC(String libName) {
schemLib = Library.findLibrary(libName);
error(schemLib==null, "Please open the PurpleFour Library");
}
public void setVddExportName(String vddNm) {vddExportName=vddNm;}
public String getVddExportName() {return vddExportName;}
public void setGndExportName(String gndNm) {gndExportName=gndNm;}
public String getGndExportName() {return gndExportName;}
public void setVddExportRole(PortCharacteristic vddRole) {
vddExportRole = vddRole;
}
public PortCharacteristic getVddExportRole() {
return vddExportRole;
}
public void setGndExportRole(PortCharacteristic gndRole) {
gndExportRole = gndRole;
}
public PortCharacteristic getGndExportRole() {
return gndExportRole;
}
//------------------------------------------------------------------------------
// Utilities for gate generators
public StdCellParams(TechType.TechTypeEnum techEnum) {
if (techEnum == TechType.TechTypeEnum.CMOS90) initCMOS90();
else if (techEnum == TechType.TechTypeEnum.MOCMOS || techEnum == TechType.TechTypeEnum.TSMC180) initMoCMOS();
else {
error(true, "Standard Cell Params does not understand technology "+techEnum);
}
this.tech = techEnum.getTechType();
}
void setOutputLibrary(Library lib) {
this.layoutLib = lib;
}
Library getOutputLibrary() { return layoutLib; }
public TechType getTechType() { return tech; }
public double getNmosWellHeight() {
return nmosWellHeight;
}
public double getPmosWellHeight() {
return pmosWellHeight;
}
public boolean getDoubleStrapGate() {
return doubleStrapGate;
}
public boolean getExhaustivePlace() {
return exhaustivePlace;
}
public int getNbPlacerPerms() {
return nbPlacerPerms;
}
public double getCellBot() {
return -nmosWellHeight;
}
public double getCellTop() {
return pmosWellHeight;
}
public double getGndY() {
return gndY;
}
public void setGndY(double y) {
gndY = y;
init();
}
public double getVddY() {
return vddY;
}
public void setVddY(double y) {
vddY = y;
init();
}
public double getGndWidth() {
return gndWidth;
}
public double getVddWidth() {
return vddWidth;
}
/** Gets the Y coordinate of the ith available track. Available
* tracks exclude Vdd, Gnd, and reserved tracks. */
public double getTrackY(int i) {
error(i==0, "StdCellParams.getTrackY: 0 is an illegal track index");
return i>0
? pmosTracks.get(i - 1).doubleValue()
: nmosTracks.get(-i - 1).doubleValue();
}
/** A physical track number enumerates all tracks regardless of
* whether the track is blocked. The value 0 is illegal. Tracks 1
* and higher are PMOS tracks. Tracks -1 and lower are NMOS
* tracks */
public double getPhysTrackY(int i) {
error(i==0, "StdCellParams.getPhysTrackY: 0 is illegal track index");
return i>0
? trackPitch/2 + (i-1)*trackPitch
: -trackPitch/2 + (i+1)*trackPitch;
}
/*
public HashMap physTracksToYCoords(HashMap physTracks) {
HashMap yCoords = new HashMap();
for (String key : physTracks.keySet();) {
int physTrk = physTracks.get(key).intValue();
Double y = new Double(getPhysTrackY(physTrk));
yCoords.put(key, y);
}
return yCoords;
}
*/
public double getTrackPitch() {return trackPitch;}
/** Get the number of NMOS tracks. The indices of NMOS tracks
* range from -1 to -nbNmosTracks(). The value 0 is an illegal
* index. */
public int nbNmosTracks() {
return nmosTracks.size();
}
/** Get the number of PMOS tracks. The indices of PMOS tracks
* range from 1 to nbPmosTracks(). The value 0 is an illegal
* index. */
public int nbPmosTracks() {return pmosTracks.size();}
public boolean getSeparateWellTies() {return separateWellTies;}
/** Connect well ties to separate exports rather than vdd and gnd */
public void setSeparateWellTies(boolean b) {
separateWellTies = b;
init();
}
public double getNmosWellTieY() {return nmosWellTieY;}
public double getPmosWellTieY() {return pmosWellTieY;}
public double getNmosWellTieWidth() {
return separateWellTies ? trackWidth : gndWidth;
}
public double getPmosWellTieWidth() {
return separateWellTies ? trackWidth : vddWidth;
}
public String getNmosWellTieName() {
return separateWellTies ? "vsb" : gndExportName;
}
public String getPmosWellTieName() {
return separateWellTies ? "vnw" : vddExportName;
}
public PortCharacteristic getNmosWellTieRole() {
return separateWellTies ? PortCharacteristic.IN : gndExportRole;
}
public PortCharacteristic getPmosWellTieRole() {
return separateWellTies ? PortCharacteristic.IN : vddExportRole;
}
public void setSimpleName(boolean b) {simpleName = b;}
public boolean getSimpleName() {return simpleName;}
// maximum distance between well contacts
public double getWellTiePitch() {
double tieToPwellTop = 0 - getNmosWellTieY();
double tieToPwellBot = getNmosWellTieY() - -nmosWellHeight;
double tieToPwellTopBot = Math.max(tieToPwellTop, tieToPwellBot);
// Right triangle:
// hypotenuse is maxWellTieRadius
// delta Y is maxGndToWellTopBot
// delta X is nmosWellTieDistance
double nmosWellTieDistance =
Math.sqrt(
Math.pow(maxWellTieRadius, 2) - Math.pow(tieToPwellTopBot, 2));
double tieToNwellTop = pmosWellHeight - getPmosWellTieY();
double tieToNwellBot = getPmosWellTieY() - 0;
double tieToNwellTopBot = Math.max(tieToNwellTop, tieToNwellBot);
double pmosWellTieDistance =
Math.sqrt(
Math.pow(maxWellTieRadius, 2) - Math.pow(tieToNwellTopBot, 2));
double tiePitch =
2 * Math.min(nmosWellTieDistance, pmosWellTieDistance);
// Safety margin: add twice as many ties as necessary
return (int) tiePitch/2;
}
// An "enable" style Nand gate has a weak pullup. This is how much
// weaker than normal the PMOS is.
public double getEnableGateStrengthRatio() {
return enableGateStrengthRatio;
}
/** round to nearest multiple of 1/2 lambda for MoCMOS,
* nearest multiple of 0.2 for CMOS90 */
public double roundGateWidth(double w) {
if (is90nm()) {
double size = Math.rint(w * 5) / 5;
if (size < minGateWid) {
System.out.println("Warning: gate width of "+size+" too small, using "+minGateWid);
return minGateWid;
}
return size;
} else {
return Math.rint(w * 2) / 2;
}
}
/** quantize size. Temporary hack because it doesn't control errors
* precisely */
public double roundSize(double s) {
if (s == 0)
return s;
double q = quantizeSize(s);
// double e = (s - q) / s;
// double qe = Math.rint(e * 100000) / 100000;
//System.out.println("desired: "+s+" quantized: "+q+" error: "+qe);
return q;
//return ((int) (s*10+.5)) / 10.0;
}
public double roundToGrid(double s) {
return ( ((int)(s/gridResolution)) * gridResolution );
}
public void setSizeQuantizationError(double err) {
error(err >= 1, "quantization error must be less than 1.0");
error(err < 0, "quantization error must be positive");
sizeErr = err;
}
public double quantizeSize(double desiredSize) {
// express desiredSize as (mantisa * 10^exponent)
double exponent = Math.floor(Math.log(desiredSize) / Math.log(10));
// 1.0 <= mantissa < 10
double mantissa = desiredSize / Math.pow(10, exponent);
double quantMant = sizeErr!=0 ? quantizeMantissa(mantissa) : mantissa;
// now round the quantized mantissa to 3 decimal places
double roundMant = Math.rint(quantMant * 100) / 100;
return Math.pow(10, exponent) * roundMant;
}
/** Add qualifiers to Cell name to reflect StdCell parameters
* "_NH70" for NMOS region height of 70 lambda
* "_PH70" for PMOS region height of 70 lambda
* "_MW70" for maximum transistor width of 70 lambda */
public String parameterizedName(String nm) {
if (!vddExportName.equals("vdd")) nm += "_pwr";
if (simpleName) return nm;
return nm
+"_NH"+nmosWellHeight+"_PH"+pmosWellHeight
+"_MW"+maxMosWidth+"_VY"
+vddY+"_GY"+ gndY;
}
/** Add qualifiers to Cell name to reflect StdCell parameters and part strength
* "_NH70" for NMOS region height of 70 lambda
* "_PH70" for PMOS region height of 70 lambda
* "_MW70" for maximum transistor width of 70 lambda
* "_X12.5" for strength of 12.5
* "{lay}" to indicate this is a layout Cell */
public String sizedName(String nm, double sz) {
String num = "" + (sz + 1000); // Add leading zeros to size
// so Gallery sorts properly.
num = num.substring(1);
return parameterizedName(nm) + "_X" + num + "{lay}";
}
private NodeInst addNmosWell(double loX, double hiX, double y, Cell cell) {
NodeInst well =
LayoutLib.newNodeInst(tech.pwell(), (loX+hiX)/2, y-nmosWellHeight/2,
hiX-loX, nmosWellHeight, 0, cell);
well.setHardSelect();
return well;
}
private NodeInst addPmosWell(double loX, double hiX, double y, Cell cell) {
NodeInst well =
LayoutLib.newNodeInst(tech.nwell(), (loX+hiX)/2, y+pmosWellHeight/2,
hiX-loX, pmosWellHeight, 0, cell);
well.setHardSelect();
return well;
}
public NodeInst addNmosWell(double loX, double hiX, Cell cell) {
return addNmosWell(loX, hiX, 0, cell);
}
public NodeInst addPmosWell(double loX, double hiX, Cell cell) {
return addPmosWell(loX, hiX, 0, cell);
}
/** Given an array of NodeInsts in a row, add wells to both ends of
the row to bring the row to minX and maxX. */
public void addWellsForRow(
ArrayList<NodeInst> row,
double minX,
double maxX,
Cell cell) {
NodeInst first = row.get(row.size() - 1);
double rowMinX = first.getBounds().getMinX();
if (rowMinX < minX) {
addPmosWell(minX, rowMinX, first.getAnchorCenterY(), cell);
addNmosWell(minX, rowMinX, first.getAnchorCenterY(), cell);
}
NodeInst last = row.get(row.size() - 1);
double rowMaxX = last.getBounds().getMaxX();
if (rowMaxX < maxX) {
addPmosWell(rowMaxX, maxX, first.getAnchorCenterY(), cell);
addNmosWell(rowMaxX, maxX, first.getAnchorCenterY(), cell);
}
}
/** essential bounds for PMOS only cells */
public void addPstackEssentialBounds(double loX, double hiX, Cell cell) {
LayoutLib.newNodeInst(tech.essentialBounds(), loX, 0, DEF_SIZE, DEF_SIZE,
180, cell);
LayoutLib.newNodeInst(tech.essentialBounds(), hiX, pmosWellHeight, DEF_SIZE,
DEF_SIZE, 0, cell);
}
/** essential bounds for NMOS only cells */
public void addNstackEssentialBounds(double loX, double hiX, Cell cell) {
LayoutLib.newNodeInst(tech.essentialBounds(), loX, -nmosWellHeight, DEF_SIZE,
DEF_SIZE, 180, cell);
LayoutLib.newNodeInst(tech.essentialBounds(), hiX, 0, DEF_SIZE,
DEF_SIZE, 0, cell);
}
/** essential bounds for cells with both NMOS and PMOS */
public void addEssentialBounds(double loX, double hiX, Cell cell) {
LayoutLib.newNodeInst(tech.essentialBounds(), loX, -nmosWellHeight, DEF_SIZE,
DEF_SIZE, 180, cell);
LayoutLib.newNodeInst(tech.essentialBounds(), hiX, pmosWellHeight, DEF_SIZE,
DEF_SIZE, 0, cell);
}
/** Print a warning if strength is less than the minimum allowable.
* Always return at least the minimum allowable strength. */
public double checkMinStrength(
double specified,
double minAllowable,
String gateNm) {
if (specified<minAllowable) {
System.out.println("Can't make: "+gateNm+" this small: X="
+specified+", Using X="+minAllowable
+" instead");
}
return Math.max(specified, minAllowable);
}
/** Calculate the number of folds and the width of a MOS
* transistor. Given that there is a limited physical height into
* which a MOS transistor must fit, divide the total required width:
* totWid into fingers. Each finger must have width less than or
* equal to spaceAvailWid.
*
* <p> If it is possible, allocate an even number of fingers so that
* the left most and right most diffusion contacts may be connected
* to power or ground to reducing the capacitance of the inner
* switching diffusion contacts.
* @param spaceAvailWid the height in the standard cell that is
* available for the diffusion of the MOS transistor.
* @param totWid the total electrical width required.
* @param groupSz This method creates fingers in multiples of
* groupSz. For example, if groupSz is 2, then only even numbers of
* fingers are created. This is needed when one FoldedMos is
* actually going to be wired up as 2 identical, independent
* transistors, for example the 2 PMOS pullups for a 2-input NAND
* gate. */
public FoldsAndWidth calcFoldsAndWidth(
double spaceAvailWid,
double totWid,
int groupSz) {
if (totWid == 0)
return null;
double maxAvailWid = Math.min(spaceAvailWid, maxMosWidth);
int nbGroups = calcNbGroups(maxAvailWid, totWid, groupSz);
double gateWid = roundGateWidth(totWid / groupSz / nbGroups);
// If we're unfortunate, rounding up gate width causes gate's width
// to exceed space available.
if (gateWid > maxAvailWid) {
nbGroups = calcNbGroups(maxAvailWid - .5, totWid, groupSz);
gateWid = roundGateWidth(totWid / groupSz / nbGroups);
}
double physWid = Math.max(tech.getDiffContWidth(), gateWid);
if (gateWid < minGateWid)
return null;
return new FoldsAndWidth(nbGroups * groupSz, gateWid, physWid);
}
/** Fix notch errors between adjacent source/drain regions.
*
* <p>Mos transistors with source/drain regions that are too close
* to each other result in notch errors for diffusion, metal1,
* and/or select. Fix these notch errors by running diffusion,
* and/or metal1 between the adjacent diffusion regions.
* @param moss An array of adjacent FoldedMos transistors arranged
* @param fillDiffNotch*/
public void fillDiffAndSelectNotches(FoldedMos[] moss, boolean fillDiffNotch) {
error(moss.length == 0, "fillDiffAndSelectNotches: no transistors?");
FoldedMos mos = moss[0];
for (int i = 1; i < moss.length; i++) {
PortInst thisPort = moss[i].getSrcDrn(0);
PortInst prevPort =
moss[i - 1].getSrcDrn(moss[i - 1].nbSrcDrns() - 1);
fillDiffAndSelectNotch(prevPort, thisPort, mos, moss[i], fillDiffNotch);
}
}
/** Wire pmos or nmos to vdd or gnd, respectively. Add an export
* if there is none. */
public void wireVddGnd(FoldedMos[] moss, SelectSrcDrn select, Cell p) {
FoldedMos mos = moss[0];
PortInst leftDiff = mos.getSrcDrn(0);
Cell f = leftDiff.getNodeInst().getParent();
double busWid = mos instanceof FoldedPmos ? vddWidth : gndWidth;
double busY = mos instanceof FoldedPmos ? vddY : gndY;
TrackRouter net = new TrackRouterH(tech.m2(), busWid, busY, tech, p);
String exportNm = mos instanceof FoldedPmos ? vddExportName : gndExportName;
if (f.findPortProto(exportNm)==null) {
// The export doesn't yet exist. Create and export a metal2 pin
// aligned with the first diffusion.
double x = leftDiff.getBounds().getCenterX();
NodeInst pinProt = LayoutLib.newNodeInst(tech.m2pin(), x,
busY, DEF_SIZE, DEF_SIZE, 0, f);
PortInst pin = pinProt.getOnlyPortInst();
Export e = Export.newInstance(f, pin, exportNm);
PortCharacteristic role =
mos instanceof FoldedPmos ? vddExportRole : gndExportRole;
e.setCharacteristic(role);
// Connect the export to itself using a standard width power or
// ground strap. The width of this strap serves as a hint to
// Electric as to the width to use to connect to this export at
// the next level up.
LayoutLib.newArcInst(tech.m2(), busWid, pin, pin);
net.connect(pin);
}
double diffY = LayoutLib.roundCenterY(leftDiff);
double notchLoY = Math.min(busY - busWid / 2, diffY);
double notchHiY = Math.max(busY + busWid / 2, diffY);
PortInst lastDiff = null;
for (int i=0; i<moss.length; i++) {
for (int j=0; j<moss[i].nbSrcDrns(); j++) {
if (select.connectThisOne(i, j)) {
PortInst thisDiff = moss[i].getSrcDrn(j);
net.connect(thisDiff);
if (lastDiff!=null) {
// Check to see if we just created a notch.
double leftX = LayoutLib.roundCenterX(lastDiff);
double rightX = LayoutLib.roundCenterX(thisDiff);
error(leftX>rightX,
"wireVddGnd: trans not sorted left to right");
double deltaX = rightX - leftX;
if (deltaX>0
&& deltaX < m1OverhangsDiffCont*2+m1Space) {
// Fill notches, sigh! (This is starting to lose
// it's novelty value.)
//
// Make height integral number of lambdas so
// centerY will be on .5 lambda grid and connecting
// to center won't generate CIF resolution errors.
double dY = Math.ceil(notchHiY - notchLoY);
NodeInst patchNode =
LayoutLib.newNodeInst(tech.m1Node(), (leftX+rightX)/2, notchLoY+dY/2,
deltaX, dY, 0, f);
PortInst patch = patchNode.getOnlyPortInst();
LayoutLib.newArcInst(tech.m1(), DEF_SIZE, patch, thisDiff);
}
}
lastDiff = thisDiff;
}
}
}
}
public void wireVddGnd(FoldedMos mos, SelectSrcDrn select, Cell p) {
wireVddGnd(new FoldedMos[] { mos }, select, p);
}
public boolean nccEnabled() {
return schemLib != null;
}
/** Perform Network Consistency Check if the user has so requested */
public void doNCC(Cell layout, String schemNm) {
if (schemLib == null) return;
Cell schem = schemLib.findNodeProto(schemNm);
error(schem == null, "can't find schematic: " + schemNm);
NccOptions options = new NccOptions();
options.howMuchStatus = 0;
NccResults results = Ncc.compare(schem, null, layout, null, options);
error(!results.match(),
"layout not topologically identical to schematic!");
}
public static double getSize(NodeInst iconInst, VarContext context) {
Variable var = iconInst.getParameterOrVariable(ATTR_X);
if (var==null)
var = iconInst.getParameterOrVariable(ATTR_S);
if (var==null)
var = iconInst.getParameterOrVariable(ATTR_SP);
if (var==null)
var = iconInst.getParameterOrVariable(ATTR_SN);
if (var==null) {
System.out.println("can't find size, using 40");
return 40;
}
Object val = context.evalVar(var);
if (val instanceof Number) return ((Number)val).doubleValue();
error(true, "an Icon's size isn't a numeric value");
return 0;
}
/** Look for parts in layoutLib */
public Cell findPart(String partNm, double sz) {
return findPart(sizedName(partNm, sz));
}
public Cell findPart(String partNm) {
return layoutLib.findNodeProto(partNm);
}
public Cell newPart(String partNm, double sz) {
return newPart(sizedName(partNm, sz));
}
public Cell newPart(String partNm) {
error(findPart(partNm) != null, "Cell already exists: " + partNm);
Cell p = Cell.newInstance(layoutLib, partNm);
return p;
}
public static double getRightDiffX(FoldedMos m) {
return LayoutLib.roundCenterX(m.getSrcDrn(m.nbSrcDrns() - 1));
}
public static double getRightDiffX(FoldedMos[] moss) {
return getRightDiffX(getRightMos(moss));
}
/** Find the X coordinate of the right most diffusion. Objects a and
* b may be either a FoldedMos or an array of FoldedMos'es */
public static double getRightDiffX(Object a, Object b) {
FoldedMos ra = getRightMos(a);
FoldedMos rb = getRightMos(b);
return Math.max(getRightDiffX(ra), getRightDiffX(rb));
}
public static void addEssentialBoundsFromChildren(Cell cell, TechType tech) {
double loX, loY, hiX, hiY;
loX = loY = Double.MAX_VALUE;
hiX = hiY = Double.MIN_VALUE;
for (Iterator<NodeInst> it=cell.getNodes(); it.hasNext();) {
Rectangle2D b = it.next().getBounds();
loX = Math.min(loX, b.getMinX());
loY = Math.min(loY, b.getMinY());
hiX = Math.max(hiX, b.getMaxX());
hiY = Math.max(hiY, b.getMaxY());
}
LayoutLib.newNodeInst(tech.essentialBounds(), loX, loY,
DEF_SIZE, DEF_SIZE, 180, cell);
LayoutLib.newNodeInst(tech.essentialBounds(), hiX, hiY,
DEF_SIZE, DEF_SIZE, 0, cell);
}
public HashMap<String,Object> getSchemTrackAssign(Cell schem) {
HashMap<String,Object> schAsgn = new HashMap<String,Object>();
for (Iterator<PortProto> it = schem.getPorts(); it.hasNext();) {
Export e = (Export) it.next();
Object val = e.getParameterOrVariable("ATTR_track");
String key = e.getName();
if (val == null)
continue;
schAsgn.put(key, val);
}
validateTrackAssign(schAsgn, schem);
return schAsgn;
}
public void validateTrackAssign(HashMap<String,Object> asgn, Cell s) {
HashMap<Object,String> trkToExp = new HashMap<Object,String>();
for (String k : asgn.keySet()) {
Object v = asgn.get(k);
// check types of key and value
error(!(v instanceof Integer),
trkMsg(k,s)+"Value not Integer: "+v);
// range check track number
int track = ((Integer) v).intValue();
error(track==0,
trkMsg(k,s)+"Track must be <=-1 or >=1, 0 is illegal");
/*
error(track<min, trkMsg(k,s) + "Track too negative: "+track+
". Only: "+(-min)+" NMOS tracks are available in this cell.");
error(track>max, trkMsg(k,s) + "Track too positive: "+track+
". Only: "+max+" PMOS tracks are available in this cell.");
*/
String oldK = trkToExp.get(v);
if (oldK != null) {
// Issue warning if two exports assigned to the same track
System.out.println(
trkMsg(k, s)
+ "Track: "
+ v
+ " is shared by export: "
+ oldK);
}
trkToExp.put(v, k);
}
}
/** Add select node to ensure there is select surrounding the
* specified diffusion node*/
public void addSelAroundDiff(NodeProto prot, Rectangle2D diffNodeBnd, double activeGateDiff,
Cell cell) {
error(prot!=tech.pdNode() && prot!=tech.ndNode(),
"addSelectAroundDiff: only works with MOSIS CMOS diff nodes");
NodeProto sel = prot == tech.pdNode() ? tech.pselNode() : tech.nselNode();
// Note that transistors are rotated in 90 degrees with res
double w = diffNodeBnd.getWidth();
double h = diffNodeBnd.getHeight();
// add the select surround if gate is longer than contact
h += tech.selectSurroundDiffAlongGateInTrans() * 2;
if (activeGateDiff > 0)
h -= activeGateDiff;
// w =+ tech.getSelectSurroundDiffInTrans() * 2;
NodeInst node = LayoutLib.newNodeInst(sel, diffNodeBnd.getCenterX(), diffNodeBnd.getCenterY(), w, h, 0, cell);
System.out.println(node.getParent().getName() + " Node " + node + " " + h + " cent " + diffNodeBnd.getCenterY());
}
public static class SelectFill {
public final NodeInst nselNode;
public final NodeInst pselNode;
public SelectFill(NodeInst nselNode, NodeInst pselNode) {
this.nselNode = nselNode;
this.pselNode = pselNode;
}
}
/**
* This fills the cell with N/P select over current N/P select areas, and over
* poly. This assumes that all Pmos devices and PWell is at y > 0, and all
* Nmos devices and NWell is at y < 0.
* @param cell the cell to fill
@param pSelect
* @param nSelect
* @param includePoly
*/
public SelectFill fillSelect(Cell cell, boolean pSelect, boolean nSelect,
boolean includePoly) {
double selSurroundPoly = tech.getSelectSurroundOverPoly();
if (selSurroundPoly < 0) return null;// do nothing.
Rectangle2D pselectBounds = LayoutLib.getBounds(cell, Layer.Function.IMPLANTP);
Rectangle2D nselectBounds = LayoutLib.getBounds(cell, Layer.Function.IMPLANTN);
if (includePoly)
{
Rectangle2D polyBounds = LayoutLib.getBounds(cell, Layer.Function.POLY1);
polyBounds.setRect(polyBounds.getX()-selSurroundPoly,
polyBounds.getY()-selSurroundPoly, polyBounds.getWidth() + 2*selSurroundPoly,
polyBounds.getHeight() + 2*selSurroundPoly);
// merge select and poly bounds
pselectBounds = pselectBounds.createUnion(polyBounds);
nselectBounds = nselectBounds.createUnion(polyBounds);
}
// pselect above y=0, nselect below
pselectBounds.setRect(pselectBounds.getMinX(), 0,
pselectBounds.getMaxX()-pselectBounds.getMinX(), pselectBounds.getMaxY());
nselectBounds.setRect(nselectBounds.getMinX(), nselectBounds.getMinY(),
nselectBounds.getMaxX()-nselectBounds.getMinX(), -nselectBounds.getMinY());
// fill it in
pselectBounds = LayoutLib.roundBounds(pselectBounds);
nselectBounds = LayoutLib.roundBounds(nselectBounds);
NodeInst pni = null, nni = null;
if (pSelect)
{
pni = LayoutLib.newNodeInst(tech.pselNode(), pselectBounds.getCenterX(), pselectBounds.getCenterY(),
pselectBounds.getWidth(), pselectBounds.getHeight(), 0, cell);
pni.setHardSelect();
}
if (nSelect)
{
nni = LayoutLib.newNodeInst(tech.nselNode(), nselectBounds.getCenterX(), nselectBounds.getCenterY(),
nselectBounds.getWidth(), nselectBounds.getHeight(), 0, cell);
nni.setHardSelect();
}
return new SelectFill(nni, pni);
}
/**
* Note that nwellX denotes the X coord of the nwell contact cut, which
* goes in the Nwell (well that holds PMOS devices).
*/
public boolean addWellCon(SelectFill selFill, PortInst gndPort, PortInst vddPort, Cell cell) {
// see if there is space
double nselMaxY = Math.abs(selFill.nselNode.getYSize());
double pselMaxY = Math.abs(selFill.pselNode.getYSize());
boolean added = false;
double pspace = getNmosWellHeight() - nselMaxY;
// the space needed is the well contact select height plus the metal1-metal1 spacing,
// or plus the select to select spacing, whichever is greater.
double m1m1_sp = 2.4;
double psel_sp = 4.8;
double nsel_sp = 4.8;
Technology techy = cell.getTechnology();
if (pspace > (wellConSelectHeight+m1m1_sp) + 2*gridResolution) {
// there is space, create it
double pwellX = roundToGrid(gndPort.getBounds().getCenterX());
double pwellY = -roundToGrid(nselMaxY + 0.5*wellConSelectHeight + wellConSelectOffset) - gridResolution;
SizeOffset so = tech.pwm1Y().getProtoSizeOffset();
NodeInst ni = LayoutLib.newNodeInst(tech.pwm1Y(), pwellX, pwellY,
tech.pwm1Y().getDefWidth() - so.getHighXOffset() - so.getLowXOffset(),
tech.pwm1Y().getDefHeight() -so.getHighYOffset() - so.getLowYOffset(), 0, cell);
LayoutLib.newArcInst(tech.m1(), getM1TrackWidth(), ni.getOnlyPortInst(), gndPort);
added = true;
// if the space to the end of the cell is less than the select to select spacing,
// fill it in with the appropriate select.
double distFromEdge = getNmosWellHeight() - (Math.abs(pwellY) + 0.5*wellConSelectHeight);
if (distFromEdge < psel_sp) {
// get size of select layer
Poly [] polys = techy.getShapeOfNode(ni);
if (polys != null) {
Layer.Function function = Layer.Function.IMPLANTP;
Rectangle2D bounds = null;
for (int i=0; i<polys.length; i++) {
if (polys[i] == null) continue;
if (polys[i].getLayer().getFunction() == function) {
bounds = polys[i].getBox();
}
}
if (bounds != null) {
// fill over the well contact. Round up bottom of fill so it is on lambda grid
double fillBot = (int)(Math.abs(pwellY) - 0.5*wellConSelectHeight) + 1;
double fillH = getNmosWellHeight() - fillBot;
ni = LayoutLib.newNodeInst(tech.pselNode(), pwellX, -1*roundToGrid(fillH/2 + fillBot),
bounds.getWidth(), roundToGrid(fillH), 0, cell);
ni.setHardSelect();
}
}
}
}
double nspace = getPmosWellHeight() - pselMaxY;
if (nspace > (wellConSelectHeight+m1m1_sp) + 2*gridResolution) {
double nwellX = roundToGrid(vddPort.getBounds().getCenterX());
double nwellY = roundToGrid(pselMaxY + 0.5*wellConSelectHeight + wellConSelectOffset) + gridResolution;
SizeOffset so = tech.nwm1Y().getProtoSizeOffset();
NodeInst ni = LayoutLib.newNodeInst(tech.nwm1Y(), nwellX, nwellY,
tech.nwm1Y().getDefWidth() - so.getHighXOffset() - so.getLowXOffset(),
tech.nwm1Y().getDefHeight() - so.getHighYOffset() - so.getLowYOffset(), 0, cell);
LayoutLib.newArcInst(tech.m1(), getM1TrackWidth(), ni.getOnlyPortInst(), vddPort);
added = true;
// if the space to the end of the cell is less than the select to select spacing,
// fill it in with the appropriate select.
double distFromEdge = getPmosWellHeight() - (Math.abs(nwellY) + 0.5*wellConSelectHeight);
if (distFromEdge < nsel_sp) {
// get size of select layer
Poly [] polys = techy.getShapeOfNode(ni);
if (polys != null) {
Layer.Function function = Layer.Function.IMPLANTN;
Rectangle2D bounds = null;
for (int i=0; i<polys.length; i++) {
if (polys[i] == null) continue;
if (polys[i].getLayer().getFunction() == function) {
bounds = polys[i].getBox();
}
}
if (bounds != null) {
// fill over the well contact. Round up bottom of fill so it is on lambda grid
double fillBot = (int)(Math.abs(nwellY) - 0.5*wellConSelectHeight) + 1;
double fillH = getPmosWellHeight() - fillBot;
ni = LayoutLib.newNodeInst(tech.nselNode(), nwellX, roundToGrid(fillH/2 + fillBot),
bounds.getWidth(), roundToGrid(fillH), 0, cell);
ni.setHardSelect();
}
}
}
}
return added;
}
}