/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: FoldedMos.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 com.sun.electric.database.geometry.Poly; import com.sun.electric.database.hierarchy.Cell; import com.sun.electric.database.prototype.NodeProto; import com.sun.electric.database.topology.NodeInst; import com.sun.electric.database.topology.PortInst; import com.sun.electric.technology.ArcProto; import com.sun.electric.technology.PrimitiveNode; import com.sun.electric.tool.generator.layout.TechType.MosInst; import com.sun.electric.tool.Job; /** * first cut at a folded transistor generator. Transistors are rotated 90 * degrees counter clockwise so that gates are vertical. * * FoldedMos is abstract. Instantiate FoldedNmos or FoldedPmos instead. */ public abstract class FoldedMos { // -------------------------- public types ------------------------------- /** * Users use GateSpace objects to tell the FoldedMos constructors to leave * additional space between diffusion contacts and gates and between * adjacent gates. There are a total of nbFolds * (nbSeries-1) such spaces. * * <p> * The FoldedMos constructor builds FoldedMos transistors from left to * right. Just before it adds a new poly gate or a new diffusion contact * (except for the first diffusion contact) it calls GateSpace.getExtraSpace * to find out the amount of "extra space" it should leave between this new * object and the preceeding object. */ public interface GateSpace { /** * The getExtraSpace() method returns the desired amount of "extra * space" to leave between the specified objects. * * <p> * First, we define "extra space". The "normal" distance from the center * of a minimum sized diffusion contact and the center of a minimum * width transistor gate is 4 lambda. For this normal spacing we define * the "extra space" to be 0 lambda. However if the width of the * transistor is less than 5 lambda, then the distance between the * centers of the diffusion contact and the gate must be 4.5 lambda. In * that case we define the extra space to be .5 lambda. * * <p> * Similarly if the distance between the centers of two adjacent series * gates is 5 lambda then the "extraSpace" is 0 lambda. However if the * distance between the centers of two adjacent series gates is 6 lambda * then the "extra space" is 1 lambda. * * @param requiredExtraSpace * the extra space required by the design rules. Normally * this is 0 lambda. However, if the gate width is less than * 5 lambda, the requiredExtraSpace between the diffusion * contact and the gate is .5 lambda. * @param foldNdx * the index of this fold. This will range between 0 and * nbFolds-1 * @param nbFolds * the number of folds in this FoldedMos * @param spaceNdx * the index of this space within this fold. This will range * between 0 and nbGates. If spaceNdx==0 or spaceNdx==nbGates * then getSpace must return the distance between a diffusion * contact and a gate. Otherwise getSpace must return the * distance between gates spaceNdx-1 and spaceNdx. * @param nbGates * the total number of gates in this FoldedMos. * @return the desired extra space. Note that the returned extra space * must be greater than or equal to requiredExtraSpace to avoid * DRC errors. */ double getExtraSpace(double requiredExtraSpace, int foldNdx, int nbFolds, int spaceNdx, int nbGates); } // ---------------------------- private data -------------------------------- private static final GateSpace useMinSp= new GateSpace() { public double getExtraSpace(double requiredExtraSpace, int foldNdx, int nbFolds, int spaceNdx, int nbGates) { return requiredExtraSpace; } }; private PortInst[] diffVias; private MosInst[] moss; private PortInst[] internalDiffs; private double difContWid; private double gateWidth; private double physWidth; private double mosY; private TechType tech; // -------------------- protected and private methods --------------------- // after rotating 90 degrees counter clock wise private static void error(boolean pred, String msg) { Job.error(pred, msg); } private boolean isPmos() { return this instanceof FoldedPmos; } // This method is necessary to ensure that the edges of all // diffusion arcs are on the .5 lambda grid to avoid CIF resolution // errors. The method assumes that diff widths are integral and // attempts to position the ArcInst endpoints on a .5 lambda grid. // // The centers of ports may not be on .5 lambda grid for two // reasons. First, the x coordinates of the centers of diffusion // ports of a MOS transistor are ALWAYS off grid when the transistor // is on grid. Second, the y coordinate of the centers of diffusion // ports of a MOS transistor are off grid when the transistor width // is .5 plus an integer. // // The first problem requires the rounding of the x coordinate onto // the .5 lambda grid. The second problem is handled by using the y // coordinate of the contact. private void newDiffArc(ArcProto diff, double y, PortInst p1, PortInst p2) { double x1= tech.roundToGrid(LayoutLib.roundCenterX(p1)); double x2= tech.roundToGrid(LayoutLib.roundCenterX(p2)); NodeInst ni1= p1.getNodeInst(); NodeInst ni2= p2.getNodeInst(); Poly poly1= ni1.getShapeOfPort(p1.getPortProto()); Poly poly2= ni2.getShapeOfPort(p2.getPortProto()); // For CMOS90, Diff node's ports are always zero in size, so when // diff node and diffCon node are at different y positions, you // cannot always create a single arc. In CMOS90, as long as the arc is a // multiple // of 0.2 wide and the end points are at a grid multiple of 0.1, there // are no grid problems, so we can just use a track router if (!poly1.contains(x1, y) || !poly2.contains(x2, y)) { LayoutLib.newArcInst(diff, LayoutLib.DEF_SIZE, p1, p2); } else { LayoutLib .newArcInst(diff, LayoutLib.DEF_SIZE, p1, x1, y, p2, x2, y); } } // If necessary, add metal-1 to guarantee that metal-1 meets minimum area // requirements. private void addM1ForMinArea(PortInst diffContPort, double difContHei, char justify) { double diffOverM1 = (tech.getDiffContWidth()-tech.getDiffCont_m1Width())/2; double metalH = difContHei - diffOverM1*2; double metalW = tech.getDiffCont_m1Width(); double area = (metalH * metalW); if (area>=tech.getM1MinArea()) return; // Insufficient metal-1. Add some! Cell cell = diffContPort.getNodeInst().getParent(); // How high should m1 be? Round up to nearest .2 lambda. double reqH = tech.getM1MinArea() / metalW; reqH = Math.ceil(reqH / .2) * .2; double x = diffContPort.getCenter().getX(); double y = diffContPort.getCenter().getY(); if (justify=='T') { // align tops of metal-1 and diffusion contact double yHi = y + metalH/2 - metalW/2; PortInst piHi = LayoutLib.newNodeInst(tech.m1pin(), x, yHi, LayoutLib.DEF_SIZE, LayoutLib.DEF_SIZE, 0, cell).getOnlyPortInst(); LayoutLib.newArcInst(tech.m1(), metalW, diffContPort, piHi); double yLo = yHi - reqH + metalW; PortInst piLo = LayoutLib.newNodeInst(tech.m1pin(), x, yLo, LayoutLib.DEF_SIZE, LayoutLib.DEF_SIZE, 0, cell).getOnlyPortInst(); LayoutLib.newArcInst(tech.m1(), metalW, diffContPort, piLo); } else { // align bottoms of metal-1 and diffusion contact double yLo = y - metalH/2 + metalW/2; PortInst piLo = LayoutLib.newNodeInst(tech.m1pin(), x, yLo, LayoutLib.DEF_SIZE, LayoutLib.DEF_SIZE, 0, cell).getOnlyPortInst(); LayoutLib.newArcInst(tech.m1(), metalW, diffContPort, piLo); double yHi = y + reqH - metalW; PortInst piHi = LayoutLib.newNodeInst(tech.m1pin(), x, yHi, LayoutLib.DEF_SIZE, LayoutLib.DEF_SIZE, 0, cell).getOnlyPortInst(); LayoutLib.newArcInst(tech.m1(), metalW, diffContPort, piHi); } } /** * Construct a MOS transistor that has been folded to fit within a certain * height. The gates always vertical: no rotation is supported. * * <p> * Edge alignment to grid is a subtle issue. Limitations in the CIF format * require all edges to lie on the 0.5 lambda grid. The user of FoldedMos is * responsible for positioning the outer boundaries of the FoldedMos on * grid. To do this she must ensure that x is on grid, that gateWid is a * multiple of 0.5 lambda. Furthermore, if gateWid is less than the width of * a minimum-sized diffusion contact (5 lambda) then y must be on grid. If * gateWid is greater than the width of a minimum-sized diffusion contact * then (y - gateWid)/2) must be on grid. * * <p> * This constructor is responsible for positioning the internal pieces on * the 0.5 lambda grid. Most significant to the user is the positioning of * the MOS transistors when gateWidth is less than the width of a minimum * sized diffusion contact. In that case this constructor assumes the user * will place the edges of the diffusion contact on grid and moves the MOS * transistors slightly up or down to get the MOS transistor on the same * grid. * * @param type * 'N' or 'P' for NMOS or PMOS * @param x * the middle of the left most diffusion contact * @param y * the "middle" of the FoldedMos. If gateWidth is less than the * width of the minimum sized diffusion contact this will be the * middle of the diffusion contact. Otherwise this is the middle * of the MOS transistor. * @param nbFolds * the number of folds. Each "fold" consists of a left diffusion * contact, nbSeries transistors stacked in series, and a right * diffusion contact. Adjacent folds share diffusion contacts. * @param nbSeries * the number of transistors stacked in series for each fold * @param gateWidth * the width of each gate * @param gateSpace * allows the user to specify the space between diffusion * contacts and gates and between adjacent gates. null means use * the minimum spacing required by the design rules. * @param justifyDiffCont * FoldedMos always makes diffusion contacts a multiple of 5 * lambda wide. If the gateWidth is not a multiple of 5 lambda * wide then this argument specifies how the diffusion contact * should be positioned within the diffusion. The choices are * 'T', or 'B' to move the contact to the top or bottom. * Centering the contact was eliminated because if the transistor * width was .5 + an integer then the diffusion contact edges * would not be on the same .5 lambda grid as any of the edges of * the transistor thereby leading to CIF resolution errors. * @param f * the facet that will contain this FoldedMos * @param tech */ FoldedMos(char type, double x, double y, int nbFolds, int nbSeries, double gateWidth, GateSpace gateSpace, char justifyDiffCont, Cell f, TechType tech) { error(type != 'P' && type != 'N', "FoldedMos: type must be 'P' or 'N': " + type); this.tech = tech; this.gateWidth= gateWidth; physWidth= Math.max(tech.getDiffContWidth(), gateWidth); diffVias= new PortInst[nbFolds + 1]; moss= new MosInst[nbFolds * nbSeries]; internalDiffs= new PortInst[nbFolds * (nbSeries - 1)]; if (gateSpace == null) gateSpace= useMinSp; PrimitiveNode diffCont= isPmos() ? tech.pdm1() : tech.ndm1(); ArcProto diff= isPmos() ? tech.pdiff() : tech.ndiff(); NodeProto difNod= isPmos() ? tech.pdNode() : tech.ndNode(); // double foldPitch = 8 + (nbSeries - 1) * (3 + 2); int diffNdx= 0, mosNdx= 0, internalDiffNdx= 0; // Contact only needs to be multiple of 5 lambda high. Because // diffusion contact is always justified up or down and because it // is always an integral number of lambdas high, it's edges and // center are on the same .5 lambda grid as the transistor edges. double difConIncr= tech.getDiffContIncr(); difContWid= Math.max(tech.getDiffContWidth(), ((int) (gateWidth / difConIncr)) * difConIncr); double difContSlop= Math.max(0, (gateWidth - difContWid) / 2); double difContY= y; switch (justifyDiffCont) { case 'T': difContY+= difContSlop; break; case 'B': difContY-= difContSlop; break; default: error(true, "FoldedMos: justifyDiffCont must be 'T', or 'B'"); } // If the MOS width is less than the minimum width of a diffusion // contact then the diffusion contact determines the boundary of // the transistor. In this case the user will place the top and // bottom of the diffusion contact on a 0.5 lambda grid. // Therefore we need to shift the MOS transistor so that its edges // are also on a 0.5 lambda grid. I will do this by shifting the // MOS transistor down until its bottom edge is on grid. mosY= y; if (gateWidth < tech.getDiffContWidth()) { double mosBotY= y - gateWidth / 2; double misAlign= Math.IEEEremainder(mosBotY, 0.5); mosY-= misAlign; } double extraDiffPolySpace = gateWidth >= tech.getDiffContWidth() ? ( 0. ) : ( tech.getGateToDiffContSpaceDogBone() - tech.getGateToDiffContSpace() ); double viaToMosPitch = tech.getDiffContWidth()/2 + tech.getGateToDiffContSpace() + tech.getGateLength()/2; double mosToMosPitch = tech.getGateLength() + tech.getGateToGateSpace(); PortInst prevPort= null; for (int i= 0;; i++) { // put down diffusion contact PortInst newPort = LayoutLib.newNodeInst(diffCont, x, difContY, LayoutLib.DEF_SIZE, difContWid, 0, f).getOnlyPortInst(); // Add redundant diffusion as a hint of the metal-1 wire size to // use to connect to this diffusion contact. Diffusion contact is // always on grid. LayoutLib.newArcInst(diff, tech.getDiffCont_m1Width(), newPort, newPort); addM1ForMinArea(newPort, difContWid, justifyDiffCont); diffVias[diffNdx++]= newPort; // connect to previous port if (prevPort != null) { newDiffArc(diff, difContY, prevPort, newPort); } prevPort= newPort; if (i >= nbFolds) break; // exit after inserting last diff contact // series transistors for (int j= 0; j < nbSeries; j++) { double extraSp= gateSpace.getExtraSpace( j == 0 ? extraDiffPolySpace : 0, i, nbFolds, j, nbSeries); if (j == 0 && extraSp != 0) { // Fill diff notch from center of diff contact to left end // of MOS. Round up diff node width to multiple of lambda // or else center will be off .5 lambda grid. double w= Math.ceil(extraSp); NodeInst dn= LayoutLib.newNodeInst(difNod, x + w / 2, mosY, w, gateWidth, 0, f); newDiffArc(diff, difContY, prevPort, dn.getOnlyPortInst()); } x+= (j == 0 ? viaToMosPitch : mosToMosPitch) + extraSp; MosInst m= isPmos() ? tech.newPmosInst(x, mosY, gateWidth, tech.getGateLength(), f) : tech.newNmosInst(x, mosY, gateWidth, tech.getGateLength(), f); moss[mosNdx++]= m; newDiffArc(diff, difContY, prevPort, m.leftDiff()); prevPort= m.rightDiff(); if (j != 0) { internalDiffs[internalDiffNdx++]= m.leftDiff(); } } double extraSp= gateSpace.getExtraSpace(extraDiffPolySpace, i, nbFolds, nbSeries, nbSeries); x+= viaToMosPitch + extraSp; if (extraSp != 0) { // fill diff notch from right end of MOS to center of diff cont double w= Math.ceil(extraSp); NodeInst dn= LayoutLib.newNodeInst(difNod, x - w / 2, mosY, w, gateWidth, 0, f); newDiffArc(diff, difContY, prevPort, dn.getOnlyPortInst()); } } } // ----------------------------- public methods ------------------------------ /** return the width of each transistor's gate */ public double getGateWidth() { return gateWidth; } /** * when the gate is narrower than the diffusion contact return the diffusion * contact width, otherwise return the gate width */ public double getPhysWidth() { return physWidth; } /** * Get the Y coordinate of the centers of the MOS transistors. This may be * different from the Y coordinate passed into the constructor. When the MOS * transistor is narrower than the diffusion contact the MOS transistor must * be been shifted up or down to get its edges on grid */ public double getMosCenterY() { return mosY; } /** * The diffusion contact's width increases with the gateWidth but is only * large enough to surround the diffusion contact cuts */ public double getDiffContWidth() { return difContWid; } public int nbSrcDrns() { return diffVias.length; } public PortInst getSrcDrn(int col) { return diffVias[col]; } public int nbGates() { return moss.length; } public PortInst getGate(int mosNdx, char pos) { error(pos != 'T' && pos != 'B', "pos must be 'T' or 'B': " + pos); return pos == 'T' ? moss[mosNdx].topPoly() : moss[mosNdx].botPoly(); } public int nbInternalSrcDrns() { return internalDiffs.length; } /** * "Internal diffusions" are the diffusions between two series transistors. * The user may wish to connect these with "generic:Uinversal" arcs in order * to fool Electric's NCC into paralleling transistor stacks of series * transistors. */ public PortInst getInternalSrcDrn(int col) { return internalDiffs[col]; } }