package com.sun.electric.tool.generator.sclibrary; import java.awt.Color; import java.util.*; import com.sun.electric.database.geometry.EGraphics; import com.sun.electric.database.geometry.EPoint; import com.sun.electric.database.hierarchy.*; import com.sun.electric.database.topology.ArcInst; import com.sun.electric.database.topology.NodeInst; import com.sun.electric.database.variable.TextDescriptor; import com.sun.electric.database.variable.Variable; import com.sun.electric.database.variable.VarContext; import com.sun.electric.technology.PrimitiveNode; import com.sun.electric.technology.technologies.Artwork; import com.sun.electric.technology.technologies.Generic; import com.sun.electric.tool.generator.layout.GateLayoutGenerator; import com.sun.electric.tool.generator.layout.StdCellParams; import com.sun.electric.tool.generator.layout.TechType; import com.sun.electric.tool.Job; import com.sun.electric.tool.io.output.Verilog; import com.sun.electric.tool.user.User; /** * Generate a standard cell library from purple and red libraries * User: gainsley * Date: Nov 15, 2006 */ public class SCLibraryGen { private String purpleLibraryName = "purpleFour"; private String redLibraryName = "redFour"; private String scLibraryName = "sclib"; private Library purpleLibrary; private Library redLibrary; private Library scLibrary; private List<StdCellSpec> scellSpecs = new ArrayList<StdCellSpec>(); private PrimitiveNode pin = Generic.tech().invisiblePinNode; private Variable.Key sizeKey = Variable.findKey("ATTR_X"); public static final Variable.Key STANDARDCELL = Variable.newKey("ATTR_StandardCell"); public static final Variable.Key DONOTEXTRACTFORDSPF = Variable.newKey("ATTR_ExcludeFromDSPFExtraction"); private static final int blueColorIndex = EGraphics.makeIndex(Color.blue); public SCLibraryGen() {} private static class StdCellSpec { private String type; private double [] sizes; private StdCellSpec(String type, double [] sizes) { this.type = type; this.sizes = sizes; } } /* ======================================================= * Settings * ======================================================= */ /** * Set the names of the purple and red libraries. These must * be loaded when running the generation, and are used * as templates for the schematics and icons of standard cells. * @param purpleLibraryName * @param redLibraryName */ public void setPurpleRedLibs(String purpleLibraryName, String redLibraryName) { this.purpleLibraryName = purpleLibraryName; this.redLibraryName = redLibraryName; } /** * Set the name of the output standard cell library. * Defaults to "sclib". * @param name */ public void setOutputLibName(String name) { this.scLibraryName = name; } /** * Add command to generate the standard cell type * for the given space-separated list of sizes. * @param type * @param sizes */ public void addStandardCell(String type, String sizes) { sizes = sizes.trim(); if (sizes.equals("")) return; String [] ss = sizes.split("\\s+"); double [] sss = new double [ss.length]; for (int i=0; i<ss.length; i++) { sss[i] = Double.parseDouble(ss[i]); } scellSpecs.add(new StdCellSpec(type, sss)); } /** * Generates the standard cell library * @param sc standard cell parameters * @return false on error, true otherwise */ public boolean generate(StdCellParams sc) { // check for red and purple libraries purpleLibrary = Library.findLibrary(purpleLibraryName); if (purpleLibrary == null) { prErr("Purple library \""+purpleLibraryName+"\" is not loaded."); return false; } redLibrary = Library.findLibrary(redLibraryName); if (redLibrary == null) { prErr("Red library \""+redLibraryName+"\" is not loaded."); return false; } prMsg("Using purple library \""+purpleLibraryName+"\" and red library \""+redLibraryName+"\""); if (sc.getTechType() == TechType.TechTypeEnum.TSMC180.getTechType()) scLibraryName = "sclibTSMC180"; else if (sc.getTechType() == TechType.TechTypeEnum.CMOS90.getTechType()) scLibraryName = "sclibCMOS90"; scLibrary = Library.findLibrary(scLibraryName); if (scLibrary == null) { scLibrary = Library.newInstance(scLibraryName, null); prMsg("Created standard cell library "+scLibraryName); } prMsg("Using standard cell library "+scLibraryName); // dunno how to set standard cell params sc.enableNCC(purpleLibraryName); for (StdCellSpec stdcell : scellSpecs) { for (double d : stdcell.sizes) { String cellname = sc.sizedName(stdcell.type, d); cellname = cellname.substring(0, cellname.indexOf('{')); // generate layout first Cell laycell = scLibrary.findNodeProto(cellname+"{lay}"); if (laycell == null) { laycell = GateLayoutGenerator.generateCell(scLibrary, sc, stdcell.type, d); if (laycell == null) { prErr("Error creating layout cell "+stdcell.type+" of size "+d); continue; } } // generate icon next Cell iconcell = scLibrary.findNodeProto(cellname+"{ic}"); if (iconcell == null) { copyIconCell(stdcell.type, purpleLibrary, cellname, scLibrary, d); } // generate sch last Cell schcell = scLibrary.findNodeProto(cellname+"{sch}"); if (schcell == null) { copySchCell(stdcell.type, purpleLibrary, cellname, scLibrary, d); } schcell = scLibrary.findNodeProto(cellname+"{sch}"); // mark schematic as standard cell List<Cell> cells = new ArrayList<Cell>(); cells.add(schcell); markStandardCell(cells, null); } } return true; } private boolean copyIconCell(String name, Library lib, String toName, Library toLib, double size) { // check if icon already exists Cell iconcell = toLib.findNodeProto(toName+"{ic}"); Cell fromIconCell = lib.findNodeProto(name+"{ic}"); if (iconcell == null && fromIconCell != null) { iconcell = Cell.copyNodeProto(fromIconCell, toLib, toName, false); if (iconcell == null) { prErr("Unable to copy purple cell "+fromIconCell.describe(false)+" to library "+toLib); return false; } // add size text NodeInst sizeni = NodeInst.makeInstance(pin, new EPoint(0,0), 0, 0, iconcell); sizeni.newVar(Artwork.ART_MESSAGE, new Double(size), TextDescriptor.getAnnotationTextDescriptor().withColorIndex(blueColorIndex)); // change all arcs to blue for (Iterator<ArcInst> it = iconcell.getArcs(); it.hasNext(); ) { ArcInst ai = it.next(); ai.newVar(Artwork.ART_COLOR, new Integer(blueColorIndex)); } for (Iterator<NodeInst> it = iconcell.getNodes(); it.hasNext(); ) { NodeInst ni = it.next(); ni.newVar(Artwork.ART_COLOR, new Integer(blueColorIndex)); } // remove 'X' parameter if (iconcell.isParam(sizeKey)) { iconcell.getCellGroup().delParam((Variable.AttrKey)sizeKey); } } return true; } private boolean copySchCell(String name, Library lib, String toName, Library toLib, double size) { // check if sch already exists Cell schcell = toLib.findNodeProto(toName+"{sch}"); Cell fromSchCell = lib.findNodeProto(name+"{sch}"); if (schcell == null && fromSchCell != null) { schcell = Cell.copyNodeProto(fromSchCell, toLib, toName, false); if (schcell == null) { prErr("Unable to copy purple cell "+fromSchCell.describe(false)+" to library "+toLib); return false; } // replace master icon cell in schematic Cell iconcell = toLib.findNodeProto(toName+"{ic}"); Cell fromIconCell = lib.findNodeProto(name+"{ic}"); if (iconcell != null && fromIconCell != null) { for (Iterator<NodeInst> it = schcell.getNodes(); it.hasNext(); ) { NodeInst ni = it.next(); if (ni.isCellInstance()) { Cell np = (Cell)ni.getProto(); if (np == fromIconCell) { ni.replace(iconcell, true, true); } } } } // remove 'X' parameter if (schcell.isParam(sizeKey)) { schcell.getCellGroup().delParam((Variable.AttrKey)sizeKey); } // remove verilog template attribute if (schcell.getVar(Verilog.VERILOG_TEMPLATE_KEY) != null) { schcell.delVar(Verilog.VERILOG_TEMPLATE_KEY); } // change X value on red gate for (Iterator<NodeInst> it = schcell.getNodes(); it.hasNext(); ) { NodeInst ni = it.next(); if (ni.isCellInstance()) { Cell np = (Cell)ni.getProto(); if (np.getLibrary() == redLibrary) { if (ni.isDefinedParameter(sizeKey)) { ni.updateParam(sizeKey, Double.valueOf(size)); } } if (np.isIconOf(schcell)) { // remove size attribute ni.delParameter(sizeKey); } } } } return true; } /* ======================================================= * Utility * ======================================================= */ /** * Mark the cell as a standard cell. * This version of the method performs the task in a Job. * @param standardCells a list of Cells to mark with the standard cell attribute marker. * @param notStandardCells a list of Cells to remove the standard cell attribute marker. */ public static void markStandardCellJob(List<Cell> standardCells, List<Cell> notStandardCells) { CreateVar job = new CreateVar(standardCells, notStandardCells, STANDARDCELL); job.startJob(); } /** * Mark the cell as a standard cell * @param standardCells a list of Cells to mark with the standard cell attribute marker. * @param notStandardCells a list of Cells to remove the standard cell attribute marker. */ public static void markStandardCell(List<Cell> standardCells, List<Cell> notStandardCells) { CreateVar job = new CreateVar(standardCells, notStandardCells, STANDARDCELL); job.doIt(); } private static class CreateVar extends Job { private List<Cell> standardCells; private List<Cell> notStandardCells; private Variable.Key varName; public CreateVar(List<Cell> standardCells, List<Cell> notStandardCells, Variable.Key varName) { super("Create Var", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER); this.standardCells = standardCells; this.notStandardCells = notStandardCells; this.varName = varName; } public boolean doIt() { TextDescriptor td = TextDescriptor.getCellTextDescriptor().withInterior(true).withDispPart(TextDescriptor.DispPos.NAMEVALUE); if (standardCells != null) for(Cell cell : standardCells) cell.newVar(varName, new Integer(1), td); if (notStandardCells != null) for(Cell cell : notStandardCells) cell.delVar(varName); return true; } } /** * Mark the cell to not be extracted for DSPF * @param cells the cells */ public static void markDoNotExtractForDSPFJob(List<Cell> cells) { CreateVar job = new CreateVar(cells, null, DONOTEXTRACTFORDSPF); job.startJob(); } /** * Return the standard cells in a hierarchy starting from * the specified top cell. * @param topCell the top cell in the hierarchy (sch or lay view) * @return a set of standard cells in the hierarchy */ public static Set<Cell> getStandardCellsInHierarchy(Cell topCell) { StandardCellHierarchy cells = new StandardCellHierarchy(); HierarchyEnumerator.enumerateCell(topCell, VarContext.globalContext, cells); return cells.getStandardCellsInHier(); } /** * Returns true if the cell is marked as a standard cell for Static * Timing Analysis * @param cell the cell to check * @return true if standard cell, false otherwise */ public static boolean isStandardCell(Cell cell) { Cell schcell = cell.getCellGroup().getMainSchematics(); if (schcell != null) cell = schcell; return cell.getVar(STANDARDCELL) != null; } /** * Returns true if the cell is marked to not be extracted for DSPF, * for Static Timing Analysis * @param cell the cell to check * @return true if marked to not extract for DSPF */ public static boolean isMarkedDoNotExtractForDSPF(Cell cell) { Cell schcell = cell.getCellGroup().getMainSchematics(); if (schcell != null) cell = schcell; return cell.getVar(DONOTEXTRACTFORDSPF) != null; } private void prErr(String msg) { System.out.println("Standard Cell Library Generator Error: "+msg); } // private void prWarn(String msg) { // System.out.println("Standard Cell Library Generator Warning: "+msg); // } private void prMsg(String msg) { System.out.println("Standard Cell Library Generator: "+msg); } /****************************** Standard Cell Enumerator *************************/ public static class StandardCellHierarchy extends HierarchyEnumerator.Visitor { private static final Integer standardCell = new Integer(0); private static final Integer containsStandardCell = new Integer(1); private static final Integer doesNotContainStandardCell = new Integer(2); private Map<Cell,Integer> standardCellMap = new HashMap<Cell,Integer>(); private Map<String,Cell> standardCellsByName = new HashMap<String,Cell>(); private List<VarContext> standardCellContexts = new ArrayList<VarContext>(); private Map<VarContext,VarContext> emptyCellContexts = new HashMap<VarContext,VarContext>(); private Map<VarContext,VarContext> containsStandardCellContexts = new HashMap<VarContext,VarContext>(); private Set<Cell> doNotExtractForDSPF = new HashSet<Cell>(); private boolean nameConflict = false; public boolean enterCell(HierarchyEnumerator.CellInfo info) { Cell cell = info.getCell(); if (isMarkedDoNotExtractForDSPF(cell)) doNotExtractForDSPF.add(cell); // skip cached and does not contain standard cell // (we want to traverse all hierarchy that contains standard cells // to produce the standard cell contexts list) if (standardCellMap.get(cell) == doesNotContainStandardCell) { emptyCellContexts.put(info.getContext(), info.getContext()); return false; } if (SCLibraryGen.isStandardCell(cell)) { standardCellContexts.add(info.getContext()); if (!standardCellMap.containsKey(cell)) { standardCellMap.put(cell, standardCell); // check for name conflict Cell otherCell = standardCellsByName.get(cell.getName()); if (otherCell != null && otherCell != cell) { System.out.println("Error: multiple standard cells with same name not allowed: "+ cell.libDescribe()+" and "+ otherCell.libDescribe()); nameConflict = true; } else { standardCellsByName.put(cell.getName(), cell); } } return false; } return true; } public void exitCell(HierarchyEnumerator.CellInfo info) { Cell cell = info.getCell(); VarContext context = info.getContext(); for (Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); ) { NodeInst ni = it.next(); if (!ni.isCellInstance()) continue; if (ni.isIconOfParent()) continue; // standard cell tag is on schematic view Cell proto = ni.getProtoEquivalent(); if (proto == null) proto = (Cell)ni.getProto(); if (containsStandardCell(proto) || standardCellMap.get(proto) == standardCell) { standardCellMap.put(cell, containsStandardCell); containsStandardCellContexts.put(context, context); return; } } standardCellMap.put(cell, doesNotContainStandardCell); emptyCellContexts.put(context, context); for (Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); ) { NodeInst ni = it.next(); if (!ni.isCellInstance()) continue; if (ni.isIconOfParent()) continue; Cell proto = ni.getProtoEquivalent(); if (proto == null) proto = (Cell)ni.getProto(); if (standardCellMap.get(proto) == doesNotContainStandardCell) { VarContext nicontext = context.push(ni); emptyCellContexts.remove(nicontext); } } } public boolean visitNodeInst(Nodable ni, HierarchyEnumerator.CellInfo info) { return true; } /** * True if the cell contains standard cells (but false if it does not, * or if it is a standard cell. * @param cell the cell in question * @return true if the clel contains a standard cell, false otherwise. */ public boolean containsStandardCell(Cell cell) { if (standardCellMap.get(cell) == containsStandardCell) return true; return false; } /** * Get the standard cells in the hiearchy after the hierarchy has * been enumerated * @return a set of the standard cells */ public Set<Cell> getStandardCellsInHier() { return getCells(standardCell); } /** * Get the cells that contain standard cells * in the hiearchy after the hierarchy has * been enumerated * @return a set of the cells that contain standard cells */ public Set<Cell> getContainsStandardCellsInHier() { return getCells(containsStandardCell); } /** * Get the cells that do not contain standard cells * in the hiearchy after the hierarchy has * been enumerated * @return a set of the cells that contain standard cells */ public Set<Cell> getDoesNotContainStandardCellsInHier() { return getCells(doesNotContainStandardCell); } /** * Returns true if there was a name conflict, where two standard * cells from different libraries have the same name. * @return true if name conflict exists */ public boolean getNameConflict() { return nameConflict; } public List<VarContext> getStandardCellsContextsInHier() { return standardCellContexts; } public Set<String> getContainsStandardCellContextsInHier() { Set<VarContext> contexts = containsStandardCellContexts.keySet(); Set<String> sorted = new TreeSet<String>(); for (VarContext context : contexts) { String s = context.getInstPath("/"); sorted.add(s); } return sorted; } /** * Get the contexts of cells that do not contain standard cells * in the hierarchy. This set is minimal. * @return a set of var contexts */ public Set<String> getEmptyCellContextsInHier() { Set<VarContext> contexts = emptyCellContexts.keySet(); Set<String> sorted = new TreeSet<String>(); for (VarContext context : contexts) { String s = context.getInstPath("/"); sorted.add(s); } return sorted; } /** * Get cells that have been marked to not be extracted for DSPF * @return the cells */ public Set<Cell> getCellsMarkedToNotExtractForDSPF() { return doNotExtractForDSPF; } /** * Get cells of the given type. The type is one of * Type.StandardCell * Type.ContainsStandardCell * Type.DoesNotContainStandardCell * @param type the type of cells to get * @return a set of cells */ private Set<Cell> getCells(Integer type) { TreeSet<Cell> set = new TreeSet<Cell>(); for (Map.Entry<Cell,Integer> entry : standardCellMap.entrySet()) { if (entry.getValue() == type) set.add(entry.getKey()); } return set; } } }