/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: Topology.java * Input/output tool: superclass for output modules that write connectivity. * Written by Steven M. Rubin, Sun Microsystems. * * 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.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.hierarchy.View; import com.sun.electric.database.network.Global; import com.sun.electric.database.network.Netlist; import com.sun.electric.database.network.Network; import com.sun.electric.database.network.NetworkTool; 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.Connection; 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.PrimitiveNode; import com.sun.electric.technology.PrimitivePort; import com.sun.electric.tool.generator.sclibrary.SCLibraryGen; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * This is the Simulation Interface tool. */ public abstract class Topology extends Output { private static final boolean DEBUGTOPOLOGY = false; /** top-level cell being processed */ protected Cell topCell; /** Map of all CellTopologies */ private Map<String,CellNetInfo> cellTopos; /** Map of all Cell names */ private Map<Cell,String> cellNameMap; private HierarchyEnumerator.CellInfo lastInfo; /** Creates a new instance of Topology */ public Topology() {} /** * Write cell to file * @return true on error */ public boolean writeCell(Cell cell, VarContext context) { writeCell(cell, context, new Visitor(this)); return false; } /** * Write cell to file * @return true on error */ public boolean writeCell(Cell cell, VarContext context, Visitor visitor) { // remember the top-level cell topCell = cell; // clear the map of CellNetInfo for each processed cell cellTopos = new HashMap<String,CellNetInfo>(); // make a map of cell names to use (unique across libraries) cellNameMap = makeCellNameMap(topCell); // write out cells start(); HierarchyEnumerator.enumerateCell(cell, context, visitor, getShortResistors()); done(); return false; } /** Abstract method called before hierarchy traversal */ protected abstract void start(); /** Abstract method called after traversal */ protected abstract void done(); /** Called at the end of the enter cell phase of hierarchy enumeration */ protected void enterCell(HierarchyEnumerator.CellInfo info) { } /** Abstract method to write CellGeom to disk */ protected abstract void writeCellTopology(Cell cell, String cellName, CellNetInfo cni, VarContext context, Topology.MyCellInfo info); /** Abstract method to convert a network name to one that is safe for this format */ protected abstract String getSafeNetName(String name, boolean bus); /** Abstract method to convert a cell name to one that is safe for this format */ protected abstract String getSafeCellName(String name); /** Abstract method to return the proper name of Power (or null to use existing name) */ protected abstract String getPowerName(Network net); /** Abstract method to return the proper name of Ground (or null to use existing name) */ protected abstract String getGroundName(Network net); /** Abstract method to return the proper name of a Global signal */ protected abstract String getGlobalName(Global glob); /** * Abstract method to decide whether export names take precedence over * arc names when determining the name of the network. */ protected abstract boolean isNetworksUseExportedNames(); /** Abstract method to decide whether library names are always prepended to cell names. */ protected abstract boolean isLibraryNameAlwaysAddedToCellName(); /** Abstract method to decide whether aggregate names (busses) are used. */ protected abstract boolean isAggregateNamesSupported(); /** Abstract method to decide whether aggregate names (busses) can have gaps in their ranges. */ protected abstract boolean isAggregateNameGapsSupported(); /** Method to decide whether to choose best export name among exports connected to signal. */ protected boolean isChooseBestExportName() { return true; } /** Abstract method to decide whether aggregate names (busses) are used. */ protected abstract boolean isSeparateInputAndOutput(); /** Abstract method to decide whether netlister is case-sensitive (Verilog) or not (Spice). */ protected abstract boolean isCaseSensitive(); /** * If the netlister has requirments not to netlist certain cells and their * subcells, override this method. */ protected boolean skipCellAndSubcells(Cell cell) { return false; } /** * Method to tell whether the netlister should write a separate copy of schematic cells for each icon. * * If you want this to happen, you must do these things: * (1) override this method and have it return true. * (2) At the place where the cell name is written, use this code: * // make sure to use the correct icon cell name if there are more than one * String subCellName = subCni.getParameterizedName(); * NodeInst ni = no.getNodeInst(); * if (ni != null) * { * String alternateSubCellName = getIconCellName((Cell)ni.getProto()); * if (alternateSubCellName != null) subCellName = alternateSubCellName; * } */ protected boolean isWriteCopyForEachIcon() { return false; } /** * If a cell is skipped, this method can perform any checking to * validate that no error occurs */ protected void validateSkippedCell(HierarchyEnumerator.CellInfo info) { } /** * Method to tell whether the topological analysis should mangle cell names that are parameterized. */ protected boolean canParameterizeNames() { return false; } /** Tell the Hierarchy enumerator how to short resistors */ protected Netlist.ShortResistors getShortResistors() { return Netlist.ShortResistors.NO; } /** Tell the Hierarchy enumerator whether or not to short parasitic resistors */ protected boolean isShortResistors() { return getShortResistors() != Netlist.ShortResistors.NO; } /** Tell the Hierarchy enumerator whether or not to short explicit (poly) resistors */ protected boolean isShortExplicitResistors() { return getShortResistors() == Netlist.ShortResistors.ALL; } /** * Method to tell set a limit on the number of characters in a name. * @return the limit to name size (0 if no limit). */ protected int maxNameLength() { return 0; } /** Abstract method to convert a cell name to one that is safe for this format */ protected CellNetInfo getCellNetInfo(String cellName) { CellNetInfo cni = cellTopos.get(cellName); return cni; } /** Used to switch from schematic enumeration to layout enumeration */ protected boolean enumerateLayoutView(Cell cell) { return false; } //------------------ override for HierarchyEnumerator.Visitor ---------------------- public class MyCellInfo extends HierarchyEnumerator.CellInfo { String currentInstanceParametizedName; } public class Visitor extends HierarchyEnumerator.Visitor { /** Topology object this Visitor is enumerating for */ private Topology outGeom; public Visitor(Topology outGeom) { this.outGeom = outGeom; } public boolean enterCell(HierarchyEnumerator.CellInfo info) { if (skipCellAndSubcells(info.getCell())) { if (!info.isRootCell()) { // save subcell topology, even though the cell isn't being written HierarchyEnumerator.CellInfo parentInfo = info.getParentInfo(); Nodable no = info.getParentInst(); String parameterizedName = parameterizedName(no, parentInfo.getContext()); CellNetInfo cni = getNetworkInformation(info.getCell(), false, parameterizedName, isNetworksUseExportedNames(), info); cellTopos.put(parameterizedName, cni); } validateSkippedCell(info); return false; } outGeom.enterCell(info); return true; } public void exitCell(HierarchyEnumerator.CellInfo info) { // write cell Cell cell = info.getCell(); CellNetInfo cni = null; if (info.isRootCell()) { cni = getNetworkInformation(cell, false, getSafeCellName(cell.getName()), isNetworksUseExportedNames(), info); cellTopos.put(cell.getName(), cni); } else { // derived parameterized name of instance of this cell in parent HierarchyEnumerator.CellInfo parentInfo = info.getParentInfo(); Nodable no = info.getParentInst(); String parameterizedName = parameterizedName(no, parentInfo.getContext()); cni = getNetworkInformation(info.getCell(), false, parameterizedName, isNetworksUseExportedNames(), info); cellTopos.put(parameterizedName, cni); } String cellName = cni.getParameterizedName(); outGeom.writeCellTopology(cell, cellName, cni, info.getContext(), (MyCellInfo)info); // see if there are alternate icons with different names if (isWriteCopyForEachIcon() && cell != topCell) { for(Iterator<Cell> it = cell.getCellGroup().getCells(); it.hasNext(); ) { Cell otherCell = it.next(); if (otherCell.getView() != View.ICON) continue; if (otherCell.getName().equals(cell.getName())) continue; String otherCellName = getSafeCellName(otherCell.getName()); if (isLibraryNameAlwaysAddedToCellName()) { if (otherCellName.startsWith("_")) otherCellName = "_" + otherCellName; else otherCellName = "__" + otherCellName; otherCellName = otherCell.getLibrary().getName() + otherCellName; } if (otherCellName.equals(cellName)) continue; outGeom.writeCellTopology(cell, otherCellName, cni, info.getContext(), (MyCellInfo)info); } } lastInfo = info; } public boolean visitNodeInst(Nodable no, HierarchyEnumerator.CellInfo info) { if (!no.isCellInstance()) return false; VarContext context = info.getContext(); String parameterizedName = parameterizedName(no, context); if (cellTopos.containsKey(parameterizedName)) return false; // already processed this Cell Cell cell = (Cell)no.getProto(); Cell schcell = cell.contentsView(); if (schcell == null) schcell = cell; if (cell.isSchematic() && enumerateLayoutView(schcell)) { Cell layCell = null; for (Iterator<Cell> it = cell.getCellGroup().getCells(); it.hasNext(); ) { Cell c = it.next(); if (c.getView() == View.LAYOUT) { layCell = c; break; } } if (layCell != null) { HierarchyEnumerator.enumerateCell(layCell, context, this); // save subcell topology, even though the cell isn't being written CellNetInfo cni = getNetworkInformation(layCell, false, layCell.getName(), isNetworksUseExportedNames(), lastInfo); cellTopos.put(parameterizedName, cni); return false; } } // else just a cell return true; } public HierarchyEnumerator.CellInfo newCellInfo() { return new MyCellInfo(); } } //---------------------------- Utility Methods -------------------------------------- /** * Class to describe a single signal in a Cell. */ protected static class CellSignal { /** name to use for this network. */ private String name; /** Network for this signal. */ private Network net; /** CellAggregate that this is part of. */ private CellAggregateSignal aggregateSignal; /** export that this is part of. */ private Export pp; /** if export is bussed, index of signal */ private int ppIndex; /** true if part of a descending bus */ private boolean descending; /** true if a power signal */ private boolean power; /** true if a ground signal */ private boolean ground; /** true if from a global signal */ private Global globalSignal; protected String getName() { return name; } protected Network getNetwork() { return net; } protected Export getExport() { return pp; } protected int getExportIndex() { return ppIndex; } protected CellAggregateSignal getAggregateSignal() { return aggregateSignal; } protected boolean isDescending() { return descending; } protected boolean isGlobal() { return globalSignal != null; } protected Global getGlobal() { return globalSignal; } protected boolean isPower() { return power; } protected boolean isGround() { return ground; } protected boolean isExported() { return pp != null; } } /** * Class to describe an aggregate signal (a bus) in a Cell. * The difference between aggregate signals and busses is that aggregate signals * are more sensitive to variations. For example, a bus may be X[1,2,4,5] but * there may be two aggregate signals describing it because of the noncontinuity * of the indices (thus, the aggregate signals will be X[1:2] and X_1[4:5]). * Some netlisters allow for gaps, in which case they will be described with a * single aggregate signal. Other factors that cause busses to be broken into * multiple aggregate signals are: * (1) differences in port direction (mixes of input and output, for example) * (2) differences in port type (power, ground, global, etc.) */ protected static class CellAggregateSignal { private String name; private Export pp; private int ppIndex; private int low; private int high; private int[] indices; private boolean supply; private boolean descending; private CellSignal [] signals; /** scratch word for the subclass */ private int flags; protected String getName() { return name; } protected String getNameWithIndices() { if (low > high) return name; if (indices != null) { StringBuffer sb = new StringBuffer(); sb.append(name); sb.append('['); for(int i=0; i<indices.length; i++) { if (i != 0) sb.append(','); sb.append(indices[i]); } sb.append(']'); return sb.toString(); } int lowIndex = low, highIndex = high; if (descending) { lowIndex = high; highIndex = low; } return name + "[" + lowIndex + ":" + highIndex + "]"; } protected int getNumSignals() { return signals.length; } protected CellSignal getSignal(int index) { return signals[index]; } protected boolean isDescending() { return descending; } protected boolean isSupply() { return supply; } protected Export getExport() { return pp; } protected int getExportIndex() { return ppIndex; } protected int [] getIndices() { return indices; } protected int getLowIndex() { return low; } protected int getHighIndex() { return high; } protected int getFlags() { return flags; } protected void setFlags(int flags) { this.flags = flags; } protected boolean isGlobal() { int numGlobal = 0; for(int i=0; i<signals.length; i++) { CellSignal cs = signals[i]; if (cs.isGlobal()) numGlobal++; } if (numGlobal > 0 && numGlobal == signals.length) return true; return false; } } /** * Class to describe the networks in a Cell. * This contains all of the CellSignal and CellAggregateSignal information. */ protected static class CellNetInfo { private Cell cell; private String paramName; private Map<Network,CellSignal> cellSignals; private List<CellSignal> cellSignalsSorted; private List<CellAggregateSignal> cellAggregateSignals; private Network pwrNet; private Network gndNet; private Netlist netList; protected CellSignal getCellSignal(Network net) { return cellSignals.get(net); } protected Iterator<CellSignal> getCellSignals() { return cellSignalsSorted.iterator(); } protected Iterator<CellAggregateSignal> getCellAggregateSignals() { return cellAggregateSignals.iterator(); } protected String getParameterizedName() { return paramName; } protected Network getPowerNet() { return pwrNet; } protected Network getGroundNet() { return gndNet; } protected Netlist getNetList() { return netList; } protected Cell getCell() { return cell; } } private CellNetInfo getNetworkInformation(Cell cell, boolean quiet, String paramName, boolean useExportedName, HierarchyEnumerator.CellInfo info) { CellNetInfo cni = doGetNetworks(cell, quiet, paramName, useExportedName, info); if (DEBUGTOPOLOGY) { printWriter.println("********Decomposition of " + cell); printWriter.println("** Have " + cni.cellSignalsSorted.size() + " signals:"); for(Iterator<CellSignal> it = cni.getCellSignals(); it.hasNext(); ) { CellSignal cs = it.next(); printWriter.println("** Name="+cs.name+" export="+cs.pp+" index="+cs.ppIndex+" descending="+cs.descending+" power="+cs.power+" ground="+cs.ground+" global="+cs.globalSignal); } if (isAggregateNamesSupported()) { printWriter.println("** Have " + cni.cellAggregateSignals.size() + " aggregate signals:"); for(CellAggregateSignal cas : cni.cellAggregateSignals) { printWriter.print("** Name="+cas.name+", export="+cas.pp+" descending="+cas.descending); if (cas.indices != null) { printWriter.print(", indices="); for(int i=0; i<cas.indices.length; i++) { if (i != 0) printWriter.print(","); printWriter.print(cas.indices[i]); } printWriter.println(); } else { printWriter.println(", low="+cas.low+", high="+cas.high); } } } printWriter.println("********DONE WITH " + cell); } return cni; } private CellNetInfo doGetNetworks(Cell cell, boolean quiet, String paramName, boolean useExportedName, HierarchyEnumerator.CellInfo info) { // create the object with cell net information CellNetInfo cni = new CellNetInfo(); cni.cell = cell; cni.paramName = paramName; // get network information about this cell cni.netList = info.getNetlist(); Global.Set globals = cni.netList.getGlobals(); int globalSize = globals.size(); // create a map of all nets in the cell cni.cellSignals = new HashMap<Network,CellSignal>(); for(Iterator<Network> it = cni.netList.getNetworks(); it.hasNext(); ) { Network net = it.next(); // special case: ignore nets on transistor bias ports that are unconnected and unexported if (!net.isExported() && !net.getArcs().hasNext()) { Iterator<PortInst> pIt = net.getPorts(); if (pIt.hasNext()) { PortInst onlyPI = pIt.next(); PortProto pp = onlyPI.getPortProto(); if (pp instanceof PrimitivePort && !pIt.hasNext()) { // found just one primitive port on this network if (((PrimitivePort)pp).isWellPort()) continue; } } } CellSignal cs = new CellSignal(); cs.pp = null; cs.descending = !NetworkTool.isBusAscending(); cs.power = false; cs.ground = false; cs.net = net; // see if it is global cs.globalSignal = null; for(int j=0; j<globalSize; j++) { Global global = globals.get(j); if (cni.netList.getNetwork(global) == net) { cs.globalSignal = global; break; } } // name the signal if (cs.globalSignal != null) { cs.name = getGlobalName(cs.globalSignal); } else { cs.name = net.getName(); } // save it in the map of signals cni.cellSignals.put(net, cs); } // look at all busses and mark directionality, first unnamed busses, then named ones for(int j=0; j<2; j++) { for(Iterator<ArcInst> aIt = cell.getArcs(); aIt.hasNext(); ) { ArcInst ai = aIt.next(); if (ai.getNameKey().isTempname()) { // temp names are handled first, ignore them in pass 2 if (j != 0) continue; } else { // real names are handled second, ignore them in pass 1 if (j == 0) continue; } int width = cni.netList.getBusWidth(ai); if (width < 2) continue; Network [] nets = new Network[width]; String [] netNames = new String[width]; for(int i=0; i<width; i++) { nets[i] = cni.netList.getNetwork(ai, i); netNames[i] = null; if (j != 0) { String preferredName = ai.getNameKey().subname(i).toString(); for(Iterator<String> nIt = nets[i].getNames(); nIt.hasNext(); ) { String netName = nIt.next(); if (netName.equals(preferredName)) { netNames[i] = netName; break; } } } if (netNames[i] == null) netNames[i] = nets[i].getNames().next(); } setBusDirectionality(nets, netNames, cni); } } // mark exported networks and set their bus directionality for(Iterator<Export> it = cell.getExports(); it.hasNext(); ) { Export pp = it.next(); // mark every network on the bus (or just 1 network if not a bus) int portWidth = cni.netList.getBusWidth(pp); for(int i=0; i<portWidth; i++) { Network net = cni.netList.getNetwork(pp, i); CellSignal cs = cni.cellSignals.get(net); if (cs == null) continue; // if there is already an export on this signal, make sure that it is wider if (cs.pp != null) { if (isChooseBestExportName()) { int oldPortWidth = cni.netList.getBusWidth(cs.pp); if (isAggregateNamesSupported()) { // with aggregate names, the widest bus is the best, so that long runs can be emitted if (oldPortWidth >= portWidth) continue; } else { // without aggregate names, individual signal names are more important if (oldPortWidth == 1) continue; if (portWidth != 1 && oldPortWidth >= portWidth) continue; } } else { if (TextUtils.STRING_NUMBER_ORDER.compare(cs.pp.getName(), pp.getName()) <= 0) continue; } } // save this export's information cs.pp = pp; cs.ppIndex = i; if (useExportedName) { String rootName = pp.getName(); if (portWidth == 1) { cs.name = rootName; } int openPos = rootName.indexOf('['); if (openPos > 0) { rootName = rootName.substring(0, openPos); for(Iterator<String> nIt = net.getExportedNames(); nIt.hasNext(); ) { String exportNetName = nIt.next(); String exportRootName = exportNetName; openPos = exportRootName.indexOf('['); if (openPos > 0) { exportRootName = exportRootName.substring(0, openPos); if (rootName.equals(exportRootName)) { cs.name = rootName + exportNetName.substring(openPos); } } } } } } if (portWidth <= 1) continue; Network [] nets = new Network[portWidth]; String [] netNames = new String[portWidth]; for(int i=0; i<portWidth; i++) { nets[i] = cni.netList.getNetwork(pp, i); String preferredName = pp.getNameKey().subname(i).toString(); netNames[i] = null; for(Iterator<String> nIt = nets[i].getNames(); nIt.hasNext(); ) { String netName = nIt.next(); if (netName.equals(preferredName)) { netNames[i] = netName; break; } } if (netNames[i] == null) netNames[i] = nets[i].getNames().next(); } setBusDirectionality(nets, netNames, cni); } // find power and ground cni.pwrNet = cni.gndNet = null; boolean multiPwr = false, multiGnd = false; for(Iterator<PortProto> eIt = cell.getPorts(); eIt.hasNext(); ) { Export pp = (Export)eIt.next(); int portWidth = cni.netList.getBusWidth(pp); if (portWidth > 1) continue; Network subNet = cni.netList.getNetwork(pp, 0); if (pp.isPower()) { if (cni.pwrNet != null && cni.pwrNet != subNet && !multiPwr) { if (!quiet) System.out.println("Warning: multiple power networks in " + cell); multiPwr = true; } cni.pwrNet = subNet; } if (pp.isGround()) { if (cni.gndNet != null && cni.gndNet != subNet && !multiGnd) { if (!quiet) System.out.println("Warning: multiple ground networks in " + cell); multiGnd = true; } cni.gndNet = subNet; } } for(Iterator<Network> it = cni.netList.getNetworks(); it.hasNext(); ) { Network net = it.next(); CellSignal cs = cni.cellSignals.get(net); if (cs == null) continue; if (cs.globalSignal != null) { if (cs.globalSignal == Global.power) { if (cni.pwrNet != null && cni.pwrNet != net && !multiPwr) { if (!quiet) System.out.println("Warning: multiple power networks in " + cell); multiPwr = true; } cni.pwrNet = net; } if (cs.globalSignal == Global.ground) { if (cni.gndNet != null && cni.gndNet != net && !multiGnd) { if (!quiet) System.out.println("Warning: multiple ground networks in " + cell); multiGnd = true; } cni.gndNet = net; } } } for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); ) { NodeInst ni = it.next(); PrimitiveNode.Function fun = ni.getFunction(); if (fun == PrimitiveNode.Function.CONPOWER || fun == PrimitiveNode.Function.CONGROUND) { for(Iterator<Connection> cIt = ni.getConnections(); cIt.hasNext(); ) { Connection con = cIt.next(); ArcInst ai = con.getArc(); Network subNet = cni.netList.getNetwork(ai, 0); if (fun == PrimitiveNode.Function.CONPOWER) { if (cni.pwrNet != null && cni.pwrNet != subNet && !multiPwr) { if (!quiet) System.out.println("Warning: multiple power networks in " + cell); multiPwr = true; } cni.pwrNet = subNet; } else { if (cni.gndNet != null && cni.gndNet != subNet && !multiGnd) { if (!quiet) System.out.println("Warning: multiple ground networks in " + cell); multiGnd = true; } cni.gndNet = subNet; } } } } if (cni.pwrNet != null) { CellSignal cs = cni.cellSignals.get(cni.pwrNet); String powerName = getPowerName(cni.pwrNet); if (powerName != null) cs.name = powerName; cs.power = true; } if (cni.gndNet != null) { CellSignal cs = cni.cellSignals.get(cni.gndNet); String groundName = getGroundName(cni.gndNet); if (groundName != null) cs.name = groundName; cs.ground = true; } // make an array of the CellSignals cni.cellSignalsSorted = new ArrayList<CellSignal>(); for(CellSignal cs : cni.cellSignals.values()) cni.cellSignalsSorted.add(cs); // sort the networks by characteristic and name Collections.sort(cni.cellSignalsSorted, new SortNetsByName(isSeparateInputAndOutput(), isAggregateNamesSupported())); // create aggregate signals if needed if (isAggregateNamesSupported()) { // organize by name and index order cni.cellAggregateSignals = new ArrayList<CellAggregateSignal>(); int total = cni.cellSignalsSorted.size(); for(int i=0; i<total; i++) { CellSignal cs = cni.cellSignalsSorted.get(i); CellAggregateSignal cas = new CellAggregateSignal(); cas.name = unIndexedName(cs.name); cas.pp = cs.pp; cas.ppIndex = cs.ppIndex; cas.supply = cs.power | cs.ground; cas.descending = cs.descending; cas.flags = 0; cs.aggregateSignal = cas; if (cs.name.equals(cas.name)) { // single wire: set range to show that cas.low = 1; cas.high = 0; cas.signals = new CellSignal[1]; cas.signals[0] = cs; } else { boolean hasGaps = false; cas.high = cas.low = TextUtils.atoi(cs.name.substring(cas.name.length()+1)); int start = i; for(int j=i+1; j<total; j++) { CellSignal csEnd = cni.cellSignalsSorted.get(j); if (csEnd.descending != cs.descending) break; // if (cell.isSchematic() && csEnd.pp != cs.pp) break; if (csEnd.globalSignal != cs.globalSignal) break; String endName = csEnd.name; String ept = unIndexedName(endName); if (ept.equals(endName)) break; if (!ept.equals(cas.name)) break; int index = TextUtils.atoi(endName.substring(ept.length()+1)); // make sure export indices go in order if (index != cas.high+1) { if (!isAggregateNameGapsSupported()) break; hasGaps = true; } if (index > cas.high) cas.high = index; i = j; csEnd.aggregateSignal = cas; } cas.signals = new CellSignal[i-start+1]; if (hasGaps) cas.indices = new int[i-start+1]; for(int j=start; j<=i; j++) { CellSignal csEnd = cni.cellSignalsSorted.get(j); if (hasGaps) { String endName = csEnd.name; String ept = unIndexedName(endName); cas.indices[j-start] = TextUtils.atoi(endName.substring(ept.length()+1)); } cas.signals[j-start] = csEnd; } } cni.cellAggregateSignals.add(cas); } // give all signals a safe name for(CellAggregateSignal cas : cni.cellAggregateSignals) cas.name = getSafeNetName(cas.name, true); // make sure all names are unique int numNameList = cni.cellAggregateSignals.size(); for(int i=1; i<numNameList; i++) { // single signal: give it that name CellAggregateSignal cas = cni.cellAggregateSignals.get(i); // see if the name clashes for(int k=0; k<1000; k++) { String ninName = cas.name; if (k > 0) ninName = ninName + "_" + k; boolean found = false; for(int j=0; j<i; j++) { CellAggregateSignal oCas = cni.cellAggregateSignals.get(j); if (ninName.equals(oCas.name)) { found = true; break; } } if (!found) { if (k > 0) cas.name = ninName; break; } } } // put names back onto the signals for(CellAggregateSignal cas : cni.cellAggregateSignals) { if (cas.low > cas.high) { CellSignal cs = cas.signals[0]; cs.name = cas.name; } else { if (cas.indices != null) { for(int k=0; k<cas.indices.length; k++) { CellSignal cs = cas.signals[k]; cs.name = getSafeNetName(cas.name + "[" + cas.indices[k] + "]", false); } } else { for(int k=cas.low; k<=cas.high; k++) { CellSignal cs = cas.signals[k-cas.low]; cs.name = getSafeNetName(cas.name + "[" + k + "]", false); } } } } Collections.sort(cni.cellAggregateSignals, new SortAggregateNetsByName(isSeparateInputAndOutput())); } else { // just get safe net names for all cell signals for (CellSignal cs : cni.cellSignalsSorted) { cs.name = getSafeNetName(cs.name, false); } } return cni; } private void setBusDirectionality(Network [] nets, String [] netNames, CellNetInfo cni) { boolean upDir = false, downDir = false; int last = 0; int width = nets.length; for(int i=0; i<width; i++) { Network subNet = nets[i]; if (subNet == null) continue; String netName = netNames[i]; int index = -1; int charPos = 0; for(;;) { charPos = netName.indexOf('[', charPos); if (charPos < 0) break; charPos++; if (!TextUtils.isDigit(netName.charAt(charPos))) continue; index = TextUtils.atoi(netName.substring(charPos)); break; } if (index < 0) break; if (i != 0) { if (index < last) downDir = true; else if (index > last) upDir = true; } last = index; } if (upDir && downDir) return; if (!upDir && !downDir) return; // valid direction found: set it on all entries of the bus for(int i=0; i<width; i++) { Network subNet = nets[i]; CellSignal cs = cni.cellSignals.get(subNet); cs.descending = downDir; } } /** * Method to return the character position in network name "name" that is the start of indexing. * If there is no indexing ("clock"), this will point to the end of the string. * If there is simple indexing ("dog[12]"), this will point to the "[". * If the index is nonnumeric ("dog[cat]"), this will point to the end of the string. * If there are multiple indices, ("dog[12][45]") this will point to the last "[" (unless it is nonnumeric). */ protected static String unIndexedName(String name) { int len = name.length(); if (len == 0) return name; if (name.charAt(len-1) != ']') return name; int i = len - 2; for( ; i > 0; i--) { char theChr = name.charAt(i); if (theChr == '[') break; if (theChr == ':' || theChr == ',') continue; if (!TextUtils.isDigit(theChr)) break; } if (name.charAt(i) != '[') return name; return name.substring(0, i); } private static class SortNetsByName implements Comparator<CellSignal> { private boolean separateInputAndOutput, aggregateNamesSupported; SortNetsByName(boolean separateInputAndOutput, boolean aggregateNamesSupported) { this.separateInputAndOutput = separateInputAndOutput; this.aggregateNamesSupported = aggregateNamesSupported; } public int compare(CellSignal cs1, CellSignal cs2) { if ((aggregateNamesSupported || separateInputAndOutput) && (cs1.pp == null) != (cs2.pp == null)) { // one is exported and the other isn't...sort accordingly return cs1.pp == null ? 1 : -1; } if (cs1.pp != null && cs2.pp != null) { if (separateInputAndOutput) { // both are exported: sort by characteristics (if different) PortCharacteristic ch1 = cs1.pp.getCharacteristic(); PortCharacteristic ch2 = cs2.pp.getCharacteristic(); if (ch1 != ch2) return ch1.getOrder() - ch2.getOrder(); } } if (aggregateNamesSupported && cs1.descending != cs2.descending) { // one is descending and the other isn't...sort accordingly return cs1.descending ? 1 : -1; } if (aggregateNamesSupported) return TextUtils.STRING_NUMBER_ORDER.compare(cs1.name, cs2.name); // Sort by simple string comparison, otherwise it is impossible // to stitch this together with netlists from other tools return cs1.name.compareTo(cs2.name); } } private static class SortAggregateNetsByName implements Comparator<CellAggregateSignal> { private boolean separateInputAndOutput; SortAggregateNetsByName(boolean separateInputAndOutput) { this.separateInputAndOutput = separateInputAndOutput; } public int compare(CellAggregateSignal cs1, CellAggregateSignal cs2) { if ((separateInputAndOutput) && (cs1.pp == null) != (cs2.pp == null)) { // one is exported and the other isn't...sort accordingly return cs1.pp == null ? 1 : -1; } if (cs1.pp != null && cs2.pp != null) { if (separateInputAndOutput) { // both are exported: sort by characteristics (if different) PortCharacteristic ch1 = cs1.pp.getCharacteristic(); PortCharacteristic ch2 = cs2.pp.getCharacteristic(); if (ch1 != ch2) return ch1.getOrder() - ch2.getOrder(); } } // Sort by simple string comparison, otherwise it is impossible // to stitch this together with netlists from other tools return cs1.name.compareTo(cs2.name); } } /** * Method to create a parameterized name for node instance "ni". * If the node is not parameterized, returns zero. * If it returns a name, that name must be deallocated when done. */ protected String parameterizedName(Nodable no, VarContext context) { Cell cell = (Cell)no.getProto(); String uniqueCellName = getUniqueCellName(cell); if (canParameterizeNames() && no.isCellInstance()) { // if there are parameters, append them to this name Map<Variable.Key,Variable> paramValues = new HashMap<Variable.Key,Variable>(); for(Iterator<Variable> it = no.getDefinedParameters(); it.hasNext(); ) { Variable var = it.next(); paramValues.put(var.getKey(), var); } for(Variable.Key key : paramValues.keySet()) { Variable var = no.getParameter(key); String eval = var.describe(context, no.getNodeInst()); if (eval == null) continue; uniqueCellName += "-" + eval.toString(); } } // if it is over the length limit, truncate it int limit = maxNameLength(); if (limit > 0 && uniqueCellName.length() > limit) { int ckSum = 0; for(int i=0; i<uniqueCellName.length(); i++) ckSum += uniqueCellName.charAt(i); ckSum = (ckSum % 9999); uniqueCellName = uniqueCellName.substring(0, limit-10) + "-TRUNC"+ckSum; } // make it safe return getSafeCellName(uniqueCellName); } /** * Method to return the name of cell "c", given that it may be ambiguously used in multiple * libraries. Also, since this is an icon cell name, do not switch to its schematic. */ protected String getIconCellName(Cell cell) { String name = cellNameMap.get(cell); return name; } /** * Method to return the name of cell "c", given that it may be ambiguously used in multiple * libraries. */ protected String getUniqueCellName(Cell cell) { Cell contents = cell.contentsView(); if (contents != null) cell = contents; String name = cellNameMap.get(cell); if (name == null) name = getDefaultName(cell); return name; } private String getDefaultName(Cell cell) { if (isLibraryNameAlwaysAddedToCellName() && !SCLibraryGen.isStandardCell(cell)) return cell.getLibrary().getName() + "__" + cell.getName(); return cell.getName(); } private class NameMapGenerator extends HierarchyEnumerator.Visitor { private Map<Cell,String> cellNameMap; private Map<String,List<Cell>> cellNameMapReverse; private Topology topology; private Cell topCell; public NameMapGenerator(Topology topology) { cellNameMap = new HashMap<Cell,String>(); cellNameMapReverse = new HashMap<String,List<Cell>>(); this.topology = topology; topCell = null; } public boolean enterCell(HierarchyEnumerator.CellInfo info) { Cell cell = info.getCell(); if (topCell == null) topCell = cell; if (cellNameMap.containsKey(cell)) return false; // add name for this cell String name = getDefaultName(cell); cellNameMap.put(cell, name); getConflictList(name).add(cell); //System.out.println("Mapped "+cell.describe(false) + " --> " + name); // see if there are alternate icons with different names if (cell != topCell && topology.isWriteCopyForEachIcon() && cell.isSchematic()) { for(Iterator<Cell> it = cell.getCellGroup().getCells(); it.hasNext(); ) { Cell otherCell = it.next(); if (otherCell.getView() != View.ICON) continue; if (otherCell.getName().equals(cell.getName())) continue; String otherCellName = getDefaultName(otherCell); // add name for this cell if (!cellNameMap.containsKey(otherCell)) { cellNameMap.put(otherCell, otherCellName); getConflictList(otherCellName).add(otherCell); } } } return true; } private List<Cell> getConflictList(String cellname) { String properCellName = topology.isCaseSensitive() ? cellname : cellname.toLowerCase(); List<Cell> conflictList = cellNameMapReverse.get(properCellName); if (conflictList == null) { conflictList = new ArrayList<Cell>(); cellNameMapReverse.put(properCellName, conflictList); } return conflictList; } public void exitCell(HierarchyEnumerator.CellInfo info) { Cell cell = info.getCell(); if (cell == topCell) { // resolve conflicts resolveConflicts(); // if (!alwaysUseLibName) // resolveConflicts(1); // resolveConflicts(2); // resolveConflicts(3); } } public boolean visitNodeInst(Nodable no, HierarchyEnumerator.CellInfo info) { if (!no.isCellInstance()) return false; VarContext context = info.getContext(); Cell cell = (Cell)no.getProto(); Cell schcell = cell.contentsView(); if (schcell == null) schcell = cell; if (cell.isSchematic() && topology.enumerateLayoutView(schcell)) { Cell layCell = null; for (Iterator<Cell> it = cell.getCellGroup().getCells(); it.hasNext(); ) { Cell c = it.next(); if (c.getView() == View.LAYOUT) { layCell = c; break; } } if (layCell != null) { String name = getDefaultName(schcell); cellNameMap.put(schcell, name); getConflictList(name).add(schcell); //System.out.println("Mapped "+schcell.describe(false) + " --> " + name); HierarchyEnumerator.enumerateCell(layCell, context, this); return false; } } else { // special case for cells with only icons schcell = cell.contentsView(); if (cell.isIcon() && schcell == null && !cellNameMap.containsKey(cell)) { String name = getDefaultName(cell); cellNameMap.put(cell, name); getConflictList(name).add(cell); return false; } } return true; } private void resolveConflicts() { List<List<Cell>> conflictLists = new ArrayList<List<Cell>>(); for (List<Cell> conflictList : cellNameMapReverse.values()) { if (conflictList.size() <= 1) continue; // no conflict conflictLists.add(conflictList); } for (List<Cell> conflictList : conflictLists) { // see if cells in the same library have a case conflict if (!topology.isCaseSensitive()) { for(int i=0; i<conflictList.size()-1; i++) { Cell cellI = conflictList.get(i); String cellIName = cellNameMap.get(cellI); for(int j=i+1; j<conflictList.size(); j++) { Cell cellJ = conflictList.get(j); if (cellI.getLibrary() != cellJ.getLibrary()) continue; String cellJName = cellNameMap.get(cellJ); if (cellIName.equalsIgnoreCase(cellJName) && !cellIName.equals(cellJName)) { // two cells with same name, just case differences for(int append=1; append<1000; append++) { String newTestName = cellJName.toLowerCase() + "_" + append; if (cellNameMapReverse.get(newTestName) != null) continue; cellNameMap.put(cellJ, newTestName); getConflictList(newTestName).add(cellJ); conflictList.remove(j); j--; break; } } } } if (conflictList.size() <= 1) continue; } // if not using library names, resolve conflicts by using them if (!isLibraryNameAlwaysAddedToCellName()) { for(int i=0; i<conflictList.size(); i++) { Cell cell = conflictList.get(i); String cellName = cellNameMap.get(cell); String newTestName = cell.getLibrary().getName() + "__" + cellName; if (cellNameMapReverse.get(newTestName) != null) continue; cellNameMap.put(cell, newTestName); getConflictList(newTestName).add(cell); conflictList.remove(i); i--; } if (conflictList.size() <= 1) continue; } // still conflicts: try adding view abbreviations for(int i=0; i<conflictList.size(); i++) { Cell cell = conflictList.get(i); String cellName = cellNameMap.get(cell); String newTestName = cell.getLibrary().getName() + "__" + cellName + "__" + cell.getView().getAbbreviation(); if (cellNameMapReverse.get(newTestName) != null) continue; cellNameMap.put(cell, newTestName); getConflictList(newTestName).add(cell); conflictList.remove(i); i--; } if (conflictList.size() <= 1) continue; // must be an error Cell cell = conflictList.get(0); System.out.print("Error: Unable to make unique cell name for "+cell.describe(false)+ ", it conflicts with:"); for (int i=1; i<conflictList.size(); i++) System.out.print(" "+conflictList.get(i).describe(false)); System.out.println(); } } // private void resolveConflicts(int whichPass) { // List<List<Cell>> conflictLists = new ArrayList<List<Cell>>(); // for (List<Cell> conflictList : cellNameMapReverse.values()) { // if (conflictList.size() <= 1) continue; // no conflict // conflictLists.add(conflictList); // } // for (List<Cell> conflictList : conflictLists) { // // on pass 1, only cell names. If any conflicts, prepend libname // if (whichPass == 1) { // // replace name with lib + name // for (Cell cell : conflictList) { // String name = cell.getLibrary().getName() + "__" + cell.getName(); // cellNameMap.put(cell, name); // getConflictList(name).add(cell); // } // conflictList.clear(); // } // if (whichPass == 2) { // // lib + name conflicts, append view // for (Cell cell : conflictList) { // String name = cell.getLibrary().getName() + "__" + cell.getName() + "__" + cell.getView().getAbbreviation(); // cellNameMap.put(cell, name); // getConflictList(name).add(cell); // } // conflictList.clear(); // } // if (whichPass == 3) { // // must be an error // Cell cell = conflictList.get(0); // System.out.print("Error: Unable to make unique cell name for "+cell.describe(false)+ // ", it conflicts with: "); // for (int i=1; i<conflictList.size(); i++) { // System.out.print(conflictList.get(i).describe(false)+" "); // } // System.out.println(); // } // } // } } /** * determine whether any cells have name clashes in other libraries */ private Map<Cell,String> makeCellNameMap(Cell cell) { NameMapGenerator gen = new NameMapGenerator(this); HierarchyEnumerator.enumerateCell(cell, VarContext.globalContext, gen); return gen.cellNameMap; } }