/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: Sim.java * Input/output tool: ESIM, RSIM, RNL, and COSMOS output * Written by Steven M. Rubin, Sun Microsystems. * * Copyright (c) 2004 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.io.output; import com.sun.electric.database.hierarchy.Cell; import com.sun.electric.database.hierarchy.Export; import com.sun.electric.database.hierarchy.HierarchyEnumerator; import com.sun.electric.database.hierarchy.Nodable; import com.sun.electric.database.network.Netlist; import com.sun.electric.database.network.Network; import com.sun.electric.database.prototype.PortCharacteristic; import com.sun.electric.database.prototype.PortProto; import com.sun.electric.database.text.TextUtils; import com.sun.electric.database.topology.ArcInst; import com.sun.electric.database.topology.NodeInst; 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.PrimitiveNode; import com.sun.electric.technology.Technology; import com.sun.electric.technology.TransistorSize; import com.sun.electric.tool.io.FileType; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * This is the netlister for Sim. */ public class Sim extends Output { private Map<Integer,String> globalNetNames; private int globalNetVDD, globalNetGND, globalNetPhi1H, globalNetPhi1L, globalNetPhi2H, globalNetPhi2L; /** key of Variable holding COSMOS attributes. */ private static final Variable.Key COSMOS_ATTRIBUTE_KEY = Variable.newKey("SIM_cosmos_attribute"); private SimPreferences localPrefs; public static class SimPreferences extends OutputPreferences { private FileType type; SimPreferences(boolean factory, FileType type) { super(factory); this.type = type; } public Output doOutput(Cell cell, VarContext context, String filePath) { Sim out = new Sim(this); if (out.openTextOutputStream(filePath)) return out.finishWrite(); out.init(cell, filePath, type); HierarchyEnumerator.enumerateCell(cell, context, new Visitor(out, type), Netlist.ShortResistors.ALL); if (out.closeTextOutputStream()) return out.finishWrite(); System.out.println(filePath + " written"); return out.finishWrite(); } } /** * Creates a new instance of the Sim netlister. */ Sim(SimPreferences sp) { localPrefs = sp; } private static class Visitor extends HierarchyEnumerator.Visitor { private Sim generator; private FileType type; public Visitor(Sim generator, FileType type) { this.generator = generator; this.type = type; } public boolean enterCell(HierarchyEnumerator.CellInfo info) { generator.writeCellContents(info, type); return true; } public void exitCell(HierarchyEnumerator.CellInfo info) {} public boolean visitNodeInst(Nodable no, HierarchyEnumerator.CellInfo info) { return true; } } private void init(Cell cell, String netfile, FileType format) { globalNetNames = new HashMap<Integer,String>(); printWriter.println("| Cell " + cell.describe(false)); emitCopyright("| ", ""); if (localPrefs.includeDateAndVersionInOutput) { printWriter.println("| Cell created " + TextUtils.formatDate(cell.getCreationDate())); printWriter.println("| Version " + cell.getVersion() + " last revised " + TextUtils.formatDate(cell.getRevisionDate())); } if (format == FileType.COSMOS) { printWriter.println("| [e | d | p | n] gate source drain length width xpos ypos {[gsd]=attrs}"); printWriter.println("| N node D-area D-perim P-area P-perim M-area M-perim"); printWriter.println("| A node attrs"); printWriter.println("| attrs = [Sim:[In | Out | 1 | 2 | 3 | Z | U]]"); } else { printWriter.println("| [epd] gate source drain length width r xpos ypos area"); printWriter.println("| N node xpos ypos M-area P-area D-area D-perim"); } } private void writeCellContents(HierarchyEnumerator.CellInfo ci, FileType format) { Cell cell = ci.getCell(); Technology tech = cell.getTechnology(); boolean top = ci.isRootCell(); Netlist netList = ci.getNetlist(); if (format == FileType.COSMOS) printWriter.println("| cell " + cell.getName()); // if top level, cache all export names if (top) { globalNetVDD = globalNetGND = globalNetPhi1H = globalNetPhi1L = globalNetPhi2H = globalNetPhi2L = -1; for(Iterator<PortProto> it = cell.getPorts(); it.hasNext(); ) { Export e = (Export)it.next(); Network net = netList.getNetwork(e, 0); int globalNetNum = ci.getNetID(net); globalNetNames.put(new Integer(globalNetNum), e.getName()); if (e.isPower()) globalNetVDD = globalNetNum; if (e.isGround()) globalNetGND = globalNetNum; if (e.getCharacteristic() == PortCharacteristic.C1 || e.getName().startsWith("clk1") || e.getName().startsWith("phi1h")) globalNetPhi1H = globalNetNum; if (e.getCharacteristic() == PortCharacteristic.C2 || e.getName().startsWith("phi1l")) globalNetPhi1L = globalNetNum; if (e.getCharacteristic() == PortCharacteristic.C3 || e.getName().startsWith("clk2") || e.getName().startsWith("phi2h")) globalNetPhi2H = globalNetNum; if (e.getCharacteristic() == PortCharacteristic.C4 || e.getName().startsWith("phi2l")) globalNetPhi2L = globalNetNum; } if (globalNetVDD < 0) reportWarning("Warning: no power export in this cell"); if (globalNetGND < 0) reportWarning("Warning: no ground export in this cell"); } // reset the arcinst node values Set<Network> netsSeen = new HashSet<Network>(); // set every arcinst to a global node number (from inside or outside) for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); ) { ArcInst ai = it.next(); Network net = netList.getNetwork(ai, 0); if (netsSeen.contains(net)) continue; netsSeen.add(net); int globalNetNum = ci.getNetID(net); // calculate parasitics for this network double darea = 0, dperim = 0, parea = 0, pperim = 0, marea = 0, mperim = 0; for(Iterator<ArcInst> oIt = cell.getArcs(); oIt.hasNext(); ) { ArcInst oAi = oIt.next(); Network oNet = netList.getNetwork(oAi, 0); if (oNet != net) continue; // calculate true length and width of arc double width = oAi.getLambdaBaseWidth(); double length = oAi.getLambdaLength(); if (oAi.isHeadExtended()) length += width/2; if (oAi.isTailExtended()) length += width/2; if (format != FileType.COSMOS) { width = TextUtils.convertDistance(width, tech, TextUtils.UnitScale.MICRO); length = TextUtils.convertDistance(length, tech, TextUtils.UnitScale.MICRO); } // sum up area and perimeter to total ArcProto.Function fun = oAi.getProto().getFunction(); if (fun.isMetal()) { marea += length * width; mperim += 2 * (length + width); } else if (fun.isPoly()) { parea += length * width; pperim += 2 * (length + width); } else if (fun.isDiffusion()) { darea += length * width; dperim += 2 * (length + width); } } if (globalNetNum != globalNetVDD && globalNetNum != globalNetGND && (marea != 0 || parea != 0 || darea != 0)) { if (format == FileType.COSMOS) { printWriter.println("N " + makeNodeName(globalNetNum, format) + " " + TextUtils.formatDouble(darea) + " " + TextUtils.formatDouble(dperim) + " " + TextUtils.formatDouble(parea) + " " + TextUtils.formatDouble(pperim) + " " + TextUtils.formatDouble(marea) + " " + TextUtils.formatDouble(mperim)); } else { printWriter.println("N " + makeNodeName(globalNetNum, format) + " 0 0 " + TextUtils.formatDouble(marea) + " " + TextUtils.formatDouble(parea) + " " + TextUtils.formatDouble(darea) + " " + TextUtils.formatDouble(dperim)); } } } if (format == FileType.COSMOS) { // Test each arc for attributes for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); ) { ArcInst ai = it.next(); Variable var = ai.getVar(COSMOS_ATTRIBUTE_KEY); if (var != null) { Network net = netList.getNetwork(ai, 0); int globalNetNum = ci.getNetID(net); printWriter.println("A " + makeNodeName(globalNetNum, format) + " Sim:" + var.getPureValue(-1)); } } } // look at every nodeinst in the cell for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); ) { NodeInst ni = it.next(); PrimitiveNode.Function fun = ni.getFunction(); // if it is a transistor, write the information if (fun.isFET()) { Network gateNet = netList.getNetwork(ni.getTransistorGatePort()); int gate = ci.getNetID(gateNet); Network sourceNet = netList.getNetwork(ni.getTransistorSourcePort()); int source = ci.getNetID(sourceNet); Network drainNet = netList.getNetwork(ni.getTransistorDrainPort()); int drain = ci.getNetID(drainNet); String tType = "U"; if (fun.isNTypeTransistor()) tType = "e"; else if (fun.isPTypeTransistor()) tType = "p"; else tType = "d"; // determine size of transistor TransistorSize size = ni.getTransistorSize(ci.getContext()); double length = 0, width = 0; if (size.getDoubleWidth() > 0) width = size.getDoubleWidth(); if (size.getDoubleLength() > 0) length = size.getDoubleLength(); if (format == FileType.COSMOS) { // see if there is an attribute mentioned on this node String extra = ""; Variable var = ni.getVar(COSMOS_ATTRIBUTE_KEY); if (var != null) extra = " g=Sim:" + var.getPureValue(-1); extra += " " + TextUtils.formatDouble(ni.getAnchorCenterX()) + " " + TextUtils.formatDouble(ni.getAnchorCenterY()); printWriter.println(tType + " " + makeNodeName(gate, format) + " " + makeNodeName(source, format) + " " + makeNodeName(drain, format) + " " + TextUtils.formatDouble(length) + " " + TextUtils.formatDouble(width) + extra); // approximate source and drain diffusion capacitances printWriter.println("N " + makeNodeName(source, format) + " " + TextUtils.formatDouble(length * width) + " " + TextUtils.formatDouble(width) + " 0 0 0 0"); printWriter.println("N " + makeNodeName(drain, format) + " " + TextUtils.formatDouble(length * width) + " " + TextUtils.formatDouble(width) + " 0 0 0 0"); } else { width = TextUtils.convertDistance(width, tech, TextUtils.UnitScale.MICRO); length = TextUtils.convertDistance(length, tech, TextUtils.UnitScale.MICRO); printWriter.println(tType + " " + makeNodeName(gate, format) + " " + makeNodeName(source, format) + " " + makeNodeName(drain, format) + " " + TextUtils.formatDouble(length) + " " + TextUtils.formatDouble(width) + " r 0 0 " + TextUtils.formatDouble(length * width)); // approximate source and drain diffusion capacitances printWriter.println("N " + makeNodeName(source, format) + " 0 0 0 0 " + TextUtils.formatDouble(length * width) + " " + TextUtils.formatDouble(width)); printWriter.println("N " + makeNodeName(drain, format) + " 0 0 0 0 " + TextUtils.formatDouble(length * width) + " " + TextUtils.formatDouble(width)); } } } } /** * Method to generate the name of a simulation node. * Generates a string name (either a numeric node number if internal, an export * name if at the top level, or "power", "ground", etc. if special). The * "format" is the particular simuator being used. */ private String makeNodeName(int globalNetNum, FileType format) { // test for special names if (globalNetNum == globalNetVDD) return "vdd"; if (globalNetNum == globalNetGND) return "gnd"; if (globalNetNum == globalNetPhi1H) { if (format == FileType.RSIM) return "phi1h"; return "clk1"; } if (globalNetNum == globalNetPhi1L) return "phi1l"; if (globalNetNum == globalNetPhi2H) { if (format == FileType.RSIM) return "phi2h"; return "clk2"; } if (globalNetNum == globalNetPhi2L) return "phi2l"; String name = globalNetNames.get(new Integer(globalNetNum)); if (name == null) name = Integer.toString(globalNetNum); return(name); } }