/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: HierarchyEnumerator.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.database.hierarchy; import com.sun.electric.database.id.CellUsage; 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.prototype.NodeProto; import com.sun.electric.database.prototype.PortProto; import com.sun.electric.database.text.TextUtils; import com.sun.electric.database.topology.NodeInst; import com.sun.electric.database.topology.PortInst; import com.sun.electric.database.topology.ArcInst; import com.sun.electric.database.topology.Geometric; import com.sun.electric.database.variable.VarContext; import com.sun.electric.tool.Job; import com.sun.electric.technology.technologies.Generic; import java.awt.geom.AffineTransform; import java.io.Serializable; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** The HierarchyEnumerator can help programs that need to "flatten" * the design hierarchy. Examples of such programs include the logical * effort engine and routers. * * <p>The HierarchyEnumerator performs a recursive descent of * the "completely expanded" design hierarchy. The HierarchyEnumerator * brings the Visitor along with it during the excursion. * The HierarchyEnumerator doesn't build a flattened data structure, * that's the prerogative of the Visitor. The HierarchyEnumerator simply * invokes Visitor methods for each Cell instance and NodeInst. * * <p>The following example illustrates the notion of "completely * expanded". Suppose the root Cell instantiates Cell A twice, and * Cell A instantiates Cell B twice. Then the HierarchyEnumerator * visits two instances of Cell A and four instances of Cell B. */ public final class HierarchyEnumerator { /** Stores the information necessary to generate an instance name for a Part * It is sometimes important not to store the instance name as a String. * When I stored instance names as strings in NCC profiles indicated that * almost 50% of the storage space was used in these strings and 70% of the * execution time was spent generating these Strings!!! */ public static abstract class NameProxy implements Serializable { private VarContext context; private String sep; private String makePath(VarContext context, String sep) { String path = context.getInstPath(sep); if (!path.equals("")) { path += sep; } return path; } protected NameProxy(VarContext context, String sep) { this.context = context; this.sep = sep; } abstract public String leafName(); abstract public Cell leafCell(); public VarContext getContext() { return context; } public String toString() { return makePath(context, sep) + leafName(); } public String toString(int numRemoveParents) { VarContext localContext = context.removeParentContext(numRemoveParents); String ret = makePath(localContext, sep) + leafName(); return ret; } } public static class NetNameProxy extends NameProxy { static final long serialVersionUID = 0; private Network net; public String leafName() { Iterator<String> it = net.getNames(); if (it.hasNext()) { return it.next(); } return "netIndex" + net.getNetIndex(); } public Iterator<String> leafNames() { return net.getNames(); } public NetNameProxy(VarContext context, String sep, Network net) { super(context, sep); this.net = net; } public Cell leafCell() { return net.getParent(); } public Network getNet() { return net; } } public static class NodableNameProxy extends NameProxy { static final long serialVersionUID = 0; private Nodable nodable; public String leafName() { return nodable.getName(); } public NodableNameProxy(VarContext context, String sep, Nodable node) { super(context, sep); this.nodable = node; } public Cell leafCell() { return nodable.getParent(); } public Nodable getNodable() { return nodable; } } // --------------------- private data ------------------------------ private Visitor visitor; private boolean caching; private int curNetId = 0; private int cellCnt = 0; // For statistics private int instCnt = 0; // For statistics private Map<Integer, NetDescription> netIdToNetDesc = new HashMap<Integer, NetDescription>(); private HashMap<Cell, int[]> cellExternalIds = new HashMap<Cell, int[]>(); private static void error(boolean pred, String msg) { Job.error(pred, msg); } // Prevent anyone from instantiating HierarchyEnumerator. private HierarchyEnumerator() { } private int[] getExternalIds(Cell cell, Netlist netlist) { int[] externalIds = cellExternalIds.get(cell); if (externalIds != null) { return externalIds; } externalIds = new int[netlist.getNumExternalNetworks()]; cellExternalIds.put(cell, externalIds); return externalIds; } //private int nextNetID() { return netIdToNetDesc.size(); } private int[] numberNets(Cell cell, Netlist netlist, int[][] portNdxToNetIDs, CellInfo info) { int numNets = netlist.getNumNetworks(); int[] externalIds = getExternalIds(cell, netlist); int[] netNdxToNetID = new int[numNets]; int baseId = curNetId; Arrays.fill(externalIds, -1); if (portNdxToNetIDs != null) { assert portNdxToNetIDs.length == cell.getNumPorts() + 1; Global.Set globals = netlist.getGlobals(); assert portNdxToNetIDs[0].length == globals.size(); for (int i = 0; i < globals.size(); i++) { Global global = globals.get(i); int netIndex = netlist.getNetwork(global).getNetIndex(); externalIds[netIndex] = portNdxToNetIDs[0][i]; } for (int i = 0, numPorts = cell.getNumPorts(); i < numPorts; i++) { Export export = cell.getPort(i); int[] ids = portNdxToNetIDs[i + 1]; assert ids.length == export.getNameKey().busWidth(); for (int j = 0; j < ids.length; j++) { int netIndex = netlist.getNetwork(export, j).getNetIndex(); externalIds[netIndex] = ids[j]; } } for (int i = 0; i < externalIds.length; i++) { assert externalIds[i] >= 0; } baseId -= externalIds.length; } for (int i = 0; i < numNets; i++) { Network net = netlist.getNetwork(i); int localId = i; assert baseId + localId <= curNetId; if (baseId + localId == curNetId) { if (portNdxToNetIDs == null && localId < externalIds.length) { externalIds[localId] = localId; } assert curNetId == baseId + localId; netIdToNetDesc.put(curNetId++, new NetDescription(net, info)); } else if (localId >= externalIds.length || portNdxToNetIDs == null) { NetDescription nd = netIdToNetDesc.get(baseId + localId); int cmp = !net.isUsernamed() ? 1 : nd.net.isUsernamed() ? 0 : -1; if (cmp == 0 && net.isExported() != nd.net.isExported()) { cmp = net.isExported() ? -1 : 1; } if (cmp == 0) { cmp = TextUtils.STRING_NUMBER_ORDER.compare(net.getName(), nd.net.getName()); } if (cmp < 0) { nd.net = net; } } int id = localId < externalIds.length ? externalIds[localId] : baseId + localId; netNdxToNetID[i] = id; } return netNdxToNetID; } private static int[] getGlobalNetIDs(Nodable no, Netlist netlist, int[] netNdxToNetID) { Global.Set gs = netlist.getNetlist(no).getGlobals(); int[] netIDs = new int[gs.size()]; for (int i = 0; i < gs.size(); i++) { int netIndex = netlist.getNetwork(no, gs.get(i)).getNetIndex(); int netID = netNdxToNetID[netIndex]; error(netID < 0, "no netID for net"); netIDs[i] = netID; } return netIDs; } private static int[] getPortNetIDs(Nodable no, PortProto pp, Netlist netlist, int[] netNdxToNetID) { int busWidth = pp.getNameKey().busWidth(); int[] netIDs = new int[busWidth]; for (int j = 0; j < busWidth; j++) { Network net = netlist.getNetwork(no, pp, j); error(net == null, "no network for net " + pp.getNameKey()); int netIndex = net.getNetIndex(); int netID = netNdxToNetID[netIndex]; error(netID < 0, "no netID for net " + pp.getNameKey()); netIDs[j] = netID; } return netIDs; } private static int[][] buildPortMap(Netlist netlist, Nodable ni, int[] netNdxToNetID) { Cell cell = (Cell) ni.getProto(); int numPorts = cell.getNumPorts(); int[][] portNdxToNetIDs = new int[numPorts + 1][]; portNdxToNetIDs[0] = getGlobalNetIDs(ni, netlist, netNdxToNetID); for (int i = 0; i < numPorts; i++) { PortProto pp = cell.getPort(i); portNdxToNetIDs[i + 1] = getPortNetIDs(ni, pp, netlist, netNdxToNetID); } return portNdxToNetIDs; } /** portNdxToNetIDs translates an Export's index to an array of NetIDs */ private void enumerateCell(Nodable parentInst, Cell cell, VarContext context, Netlist netlist, int[][] portNdxToNetIDs, AffineTransform xformToRoot, CellInfo parent) { CellInfo info = visitor.newCellInfo(); int firstNetID = curNetId; int[] netNdxToNetID = numberNets(cell, netlist, portNdxToNetIDs, info); int lastNetIDPlusOne = curNetId; cellCnt++; info.init(parentInst, cell, context, netlist, netNdxToNetID, portNdxToNetIDs, xformToRoot, netIdToNetDesc, parent); boolean enumInsts = visitor.enterCell(info); if (!enumInsts) { return; } for (Iterator<Nodable> it = netlist.getNodables(); it.hasNext();) { Nodable ni = it.next(); instCnt++; boolean descend = visitor.visitNodeInst(ni, info); NodeProto np = ni.getProto(); if (descend && ni.isCellInstance() && !((Cell) np).isIcon()) { int[][] portNmToNetIDs2 = buildPortMap(netlist, ni, netNdxToNetID); AffineTransform xformToRoot2 = xformToRoot; if (ni instanceof NodeInst) { // add transformation from lower level xformToRoot2 = new AffineTransform(xformToRoot); xformToRoot2.concatenate(((NodeInst) ni).rotateOut()); xformToRoot2.concatenate(((NodeInst) ni).translateOut()); } enumerateCell(ni, (Cell) np, caching ? context.pushCaching(ni) : context.push(ni), netlist.getNetlist(ni), portNmToNetIDs2, xformToRoot2, info); } } visitor.exitCell(info); // release storage associated with VarContext variable cache context.deleteVariableCache(); // remove entries in netIdToNetDesc that we'll never use again for (int i = firstNetID; i < lastNetIDPlusOne; i++) { netIdToNetDesc.remove(i); } } // Set up everything for the root cell and then initiate the // hierarchical traversal. private void doIt(Cell root, VarContext context, Netlist netlist, Visitor visitor, boolean cache) { this.visitor = visitor; this.caching = cache; if (context == null) { context = VarContext.globalContext; } int[][] exportNdxToNetIDs = null; enumerateCell(null, root, context, netlist, exportNdxToNetIDs, new AffineTransform(), null); // System.out.println("A total of: " + curNetId + " nets were numbered"); // System.out.println("A total of: " + cellCnt + " Cells were visited"); // System.out.println("A total of: " + instCnt + " NodeInsts were visited"); } // ------------------------ public types --------------------------- /** Perform useful work while the HierarchyEnumerator enumerates * the design. Whereas the HierarchyEnumerator is responsible for * enumerating every Cell and NodeInst in the flattened design, * the Visitor object is responsible for performing useful work * during the enumeration. * * <p>The HierarchyEnumerator performs a recursive descent of the * design hierarchy starting with the root Cell. When the * HierarchyEnumerator enters a Cell instance it calls the Visitor's * enterCell() method to let the Visitor know that it's just started * working on a new Cell instance. Then the HierarchyEnumerator calls * the Visitor's visitNodeInst() method for each NodeInst in that * Cell. Finally, after all the NodeInsts have been visited, the * HierarchyEnumerator calls the Visitor's exitCell() method to * inform the Visitor that the HierarchyEnumerator is done with that * Cell. * * <p>The Visitor's visitNodeInst() method controls whether the * HierarchyEnumerator descends recursively into the Cell * instantiated by that NodeInst. If the visitNodeInst() method * returns true, then the HierarchyEnumerator enumerates the contents * of that NodeInst's child Cell before it continues enumerating the * NodeInsts of the current Cell. */ public static abstract class Visitor { /** A hook to allow the user to add additional information to * a CellInfo. The newCellInfo method is a "Factory" * method. If the user wishes to record additional application * specific information for each Cell, the user should extend * the CellInfo class and then override newCellInfo to return * an instance of that derived class. */ public CellInfo newCellInfo() { return new CellInfo(); } /** The HierarchyEnumerator is about to begin enumerating the * contents of a new Cell instance. That instance has just * become the new "current" Cell instance. * @param info information about the Cell instance being * enumerated * @return a boolean indicating if the HierarchyEnumerator * should enumerate the contents of the current Cell. True * means enumerate the current cell */ public abstract boolean enterCell(CellInfo info); /** The HierarchyEnumerator has finished enumerating the * contents of the current Cell instance. It is about to leave * it, never to return. The CellInfo associated with the * current Cell instance is about to be abandoned. * @param info information about the Cell instance being * enumerated */ public abstract void exitCell(CellInfo info); /** The HierarchyEnumerator is visiting Nodable ni. * @param ni the Nodable that HierarchyEnumerator is visiting. * @return a boolean indicating whether or not the * HierarchyEnumerator should expand the Cell instantiated by * ni. True means expand. If ni instantiates a PrimitiveNode * then the return value is ignored by the * HierarchyEnumerator. */ public abstract boolean visitNodeInst(Nodable ni, CellInfo info); /** Using visitNodeInst implements a Top-Down traversal. If one * wishes to implement Bottom-Up traversal, use this method instead, * or in conjunction with visitNodeInst. */ //public abstract void visitNodeInstBottomUp(Nodable ni, CellInfo info); } /** The NetDescription object provides a Network and the level of * hierarchy in which the Network occurs. The visitor can use * NetDescription to formulate, for example, the name of * the net */ public static class NetDescription { private Network net; private CellInfo info; NetDescription(Network net, CellInfo info) { this.net = net; this.info = info; } public Network getNet() { return net; } public CellInfo getCellInfo() { return info; } } /** The CellInfo object is used to pass information to the Visitor * during the enumeration. The CellInfo object contains many methods * that describe the Cell currently being enumerated and the state * of the enumeration. * * <p>The HierarchyEnumerator creates a new CellInfo for a Cell * instance just before it begins enumerating the contents of that * Cell instance. The HierarchyEnumerator abandons the CellInfo once * it is done enumerating the contents of that Cell instance. Once * the CellInfo is abandoned the garbage collector may reclaim the * CellInfo's storage. * * <p>Each CellInfo has a reference to the CellInfo of the parent of * the current Cell instance. Thus the Visitor is able to get * information about all the ancestors of the current Cell * instance. * * <p>In most cases, the user will need to store additional * information in the CellInfo. In those cases the user should * extend the CellInfo class and override the Visitor.newCellInfo() * method to return an instance of the derived class. */ public static class CellInfo { private Nodable parentInst; private Cell cell; private VarContext context; private Netlist netlist; private int[] netNdxToNetID; private int[][] exportNdxToNetIDs; private AffineTransform xformToRoot; private Map<Integer, NetDescription> netIdToNetDesc; private CellInfo parentInfo; // package private void init(Nodable parentInst, Cell cell, VarContext context, Netlist netlist, int[] netToNetID, int[][] exportNdxToNetIDs, AffineTransform xformToRoot, Map<Integer, NetDescription> netIdToNetDesc, CellInfo parentInfo) { this.parentInst = parentInst; this.cell = cell; this.context = context; this.netlist = netlist; this.netNdxToNetID = netToNetID; this.exportNdxToNetIDs = exportNdxToNetIDs; this.xformToRoot = xformToRoot; this.netIdToNetDesc = netIdToNetDesc; this.parentInfo = parentInfo; } /** * <p>Return an AffineTransform that encodes the size, rotation, and * center of this NodeInst. * * <p>The returned AffineTransform has the property that when * it is applied to a unit square centered at the origin the * result is the bounding box of the NodeInst. * This transform is useful because it can be used to * map the position of a NodeInst through levels of the design * hierarchy. * * <p>Note that the user can set the position of a NodeInst * using NodeInst.setPositionFromTransform(). For example, the * following operations make no change to a NodeInst's position: * * <code> * ni.setPositionFromTransform(ni.getPositionFromTransform()); * </code> */ // public AffineTransform getPositionAsTransform(NodeInst ni) { // AffineTransform at = new AffineTransform(); // at.setToTranslation(getAnchorCenterX(), getAnchorCenterY()); // boolean transpose = sX<0 ^ sY<0; // if (transpose){ // at.scale(1, -1); // at.rotate(Math.PI/2); // } // at.rotate(angle*Math.PI/1800); // at.scale(sX, sY); // return at; // } // // private double angleFromXY(double x, double y) { // double ans = Math.atan2(y, x) * 180/Math.PI; // //System.out.println("(x, y): ("+x+", "+y+") angle: "+ans); // return ans; // } // // private double angle0To360(double a) { // while (a >= 360) a -= 360; // while (a < 0) a += 360; // return a; // } // // private String makePath(VarContext context, String sep) { // String path = context.getInstPath(sep); // if (!path.equals("")) path+=sep; // return path; // } /** * Temporary for testing the HierarchyEnumerator. * * <p>Set the size, angle, and center of this NodeInst based upon an * affine transformation. * * <p>The AffineTransform must map a unit square centered at the * origin to the desired bounding box for the NodeInst. * * <p>Note that this operation cannot succeed for all affine * transformations. The reason is that Electric's transformations * always preserve right angles whereas, in general, affine * transformations do not. If the given affine transformation does * not preserve right angles this method will print a warning * displaying the angle that results when a right angle is * transformed. * * <p>Warning: this code is experimental * @param xForm the affine transformation. xForm must yield the * bounding box of the NodeInst when applied to a unit square * centered at the origin. */ // public void setPositionFromTransform(AffineTransform xForm) { // double sizeX, sizeY, newAngle, centX, centY; // boolean debug = false; // // if (debug) System.out.println(xForm); // // Point2D a = new Point2D.Double(0, 1); // along Y axis // Point2D b = new Point2D.Double(0, 0); // origin // Point2D c = new Point2D.Double(1, 0); // along X axis // // Point2D aP = new Point2D.Double(); // Point2D bP = new Point2D.Double(); // Point2D cP = new Point2D.Double(); // // xForm.transform(a, aP); // xForm.transform(b, bP); // xForm.transform(c, cP); // // if (debug) { // System.out.println("aP: " + aP); // System.out.println("bP: " + bP); // System.out.println("cP: " + cP); // } // // sizeX = bP.distance(cP); // sizeY = bP.distance(aP); // centX = bP.getX(); // centY = bP.getY(); // // double angleA = angleFromXY(aP.getX() - bP.getX(), aP.getY() - bP.getY()); // double angleC = angleFromXY(cP.getX() - bP.getX(), cP.getY() - bP.getY()); // double angleAC = angle0To360(angleA - angleC); // // if (debug) { // System.out.println("angleC: " + angleC); // System.out.println("angleA: " + angleA); // System.out.println("angleAC: " + angleAC); // } // // round to 1/10 degrees // angleAC = Math.rint(angleAC * 10) / 10; // if (angleAC == 90) { // newAngle = angle0To360(angleC); // } else if (angleAC == 270) { // // By using geometric constructions on paper I determined that I // // need to rotate by (270 degrees - angleC) and then transpose. // newAngle = angle0To360(270 - angleC); // sizeX = -sizeX; // Negative size means transpose (not mirror) // } else { // System.out.println("error in NodeInst.setPositionFromTransform: "+ // "angle not 90 or 270: " + angleAC); // newAngle = angleC; // } // // if (debug) System.out.println( // "setPositionFromTransform: new position {\n" // + " sizeX: " + sizeX + "\n" // + " sizeY: " + sizeY + "\n" // + " angle: " + newAngle + "\n" // + " dx: " + centX + "\n" // + " dy: " + centY + "\n" // + "}\n"); // // modifyInstance(centX-getAnchorCenterX(), centY-getAnchorCenterY(), sizeX-sX, // sizeY-sY, (int)Math.round(newAngle*10)-angle); // } /** The Cell currently being visited. */ public final Cell getCell() { return cell; } /** The Cell that is the root of the traversal */ public final boolean isRootCell() { return parentInfo == null; } /** The VarContext to use for evaluating all variables in the * current Cell. */ public final VarContext getContext() { return context; } /** The Netlist of the current Cell. */ public final Netlist getNetlist() { return netlist; } /** Get the CellInfo for the current Cell's parent. If the * current Cell is the root then return null. */ public final CellInfo getParentInfo() { return parentInfo; } /** Get the NodeInst that instantiates the Current * instance. If the current Cell is the root then return * null. */ public final Nodable getParentInst() { return parentInst; } /** Get the CellInfo for the root Cell */ public final CellInfo getRootInfo() { CellInfo i = this; while (i.getParentInfo() != null) { i = i.getParentInfo(); } return i; } /** Get netIDs for the Export: e. * @return an array of net numbers. */ public final int[] getExportNetIDs(Export e) { if (isRootCell()) { // exportNdxToNetIDs is invalid for the root Cell because // no mapping from net index to netID is performed for Exports // of the root Cell. int width = netlist.getBusWidth(e); int[] netIDs = new int[width]; for (int i = 0; i < width; i++) { netIDs[i] = netlist.getNetwork(e, i).getNetIndex(); } return netIDs; } return exportNdxToNetIDs[e.getPortIndex() + 1]; } /** Map any net inside the current cell to a net * number. During the course of the traversal, all nets that * map to the same net number are connected. Nets that map to * different net numbers are disconnected. * * <p>If you want to generate a unique name for the net use * getUniqueNetName(). */ public final int getNetID(Network net) { return getNetID(net.getNetIndex()); } /** Map a net index from the current cell to a net ID. * number. During the course of the traversal, all nets that * map to the same net number are connected. Nets that map to * different net numbers are disconnected. * @param netIndex net index within current cell * @return net ID that is unique over entire net list. */ private int getNetID(int netIndex) { return netNdxToNetID[netIndex]; } // public final boolean isGlobalNet(int netID) { // error(netID<0, "negative netIDs are illegal"); // return netID <= largestGlobalNetID; // } /** * Get the set of netIDs that are connected to the specified port of * the specified Nodable. */ public final int[] getPortNetIDs(Nodable no, PortProto pp) { return HierarchyEnumerator.getPortNetIDs(no, pp, netlist, netNdxToNetID); } /** Get a unique, flat net name for the network. The network * name will contain the hierarchical context as returned by * VarContext.getInstPath() if it is not a top-level network. * @param sep the context separator to use if needed. * @return a unique String identifier for the network */ public final String getUniqueNetName(Network net, String sep) { NameProxy proxy = getUniqueNetNameProxy(net, sep); return proxy.toString(); } /** Same as getUniqueNetName except it returns a NameProxy instead of a * String name */ public final NetNameProxy getUniqueNetNameProxy(Network net, String sep) { return getUniqueNetNameProxy(getNetID(net), sep); } /** Get a unique, flat net name for the network. The network * name will contain the hierarchical context as returned by * VarContext.getInstPath() if it is not a top-level network. * @param sep the hierarchy separator to use if needed. * @return a unique String identifier for the network */ public final String getUniqueNetName(int netID, String sep) { NameProxy proxy = getUniqueNetNameProxy(netID, sep); return proxy.toString(); } /** Same as getUniqueNetName except it returns a NameProxy instead of a * String name */ public final NetNameProxy getUniqueNetNameProxy(int netID, String sep) { NetDescription ns = netIdToNetDesc.get(netID); VarContext netContext = ns.getCellInfo().getContext(); // String leafName; // Iterator it = ns.getNet().getNames(); // if (it.hasNext()) { // leafName = (String) it.next(); // } else { // leafName = "netID"+netID; // } return new NetNameProxy(netContext, sep, ns.getNet()); } /** Get a unique, flat instance name for the Nodable. * @param no * @param sep the hierarchy separator to use if needed * @return a unique String identifer for the Nodable */ public final String getUniqueNodableName(Nodable no, String sep) { return getUniqueNodableNameProxy(no, sep).toString(); } /** Same as getUniqueNodableName except that it returns a NameProxy * instead of a String name. */ public final NodableNameProxy getUniqueNodableNameProxy(Nodable no, String sep) { return new NodableNameProxy(getContext(), sep, no); } /** Get the Network that is closest to the root in the design * hierarchy that corresponds to netID. */ public final NetDescription netIdToNetDescription(int netID) { return netIdToNetDesc.get(netID); } /** * Get the Network in the parent that connects to the specified * Network in this cell. Returns null if no network in parent connects * to this network (i.e. network is not connected to export), or null * if no parent. * @param network the network in this cell * @return the network in the parent that connects to the * specified network, or null if no such network. */ public Network getNetworkInParent(Network network) { if (parentInfo == null) { return null; } if (network == null) { return null; } if (network.getNetlist() != netlist) { return null; } return parentInfo.getNetlist().getNetwork(context.getNodable(), network); // // find export on network // boolean found = false; // Export export = null; // int i = 0; // for (Iterator<Export> it = cell.getExports(); it.hasNext(); ) { // export = it.next(); // for (i=0; i<export.getNameKey().busWidth(); i++) { // Network net = netlist.getNetwork(export, i); // if (net == network) { found = true; break; } // } // if (found) break; // } // if (found) { // // find corresponding port on icon // //System.out.println("In "+cell.describe()+" JNet "+network.describe()+" is exported as "+export.getName()+"; index "+i); // Nodable no = context.getNodable(); // PortProto pp = no.getProto().findPortProto(export.getNameKey()); // //System.out.println("Found corresponding port proto "+pp.getName()+" on cell "+no.getProto().describe()); // // find corresponding network in parent // Network parentNet = parentInfo.getNetlist().getNetwork(no, pp, i); // return parentNet; // } // // check if global network // Global.Set globals = netlist.getGlobals(); // for (i=0; i<globals.size(); i++) { // Global global = globals.get(i); // if (netlist.getNetwork(global) == network) { // // it is a global, return the global network in the parent // return parentInfo.getNetlist().getNetwork(global); // } // } // return null; } /** * Method to get the transformation from the current location to the root. * If this is at the top cell, the transformation is identity. * @return the transformation from the current location to the root. */ public AffineTransform getTransformToRoot() { return xformToRoot; } } // ----------------------- public methods -------------------------- /** Begin enumeration of the contents of the Cell root. You MUST * call rebuildNetworks() on the root Cell before calling * beginEnumeration(). * @param root the starting point of the enumeration. * @param context the VarContext for evaluating parameters in Cell * root. If context is null then VarContext.globalContext is used. * @param visitor the object responsible for doing something useful * during the enumertion of the design hierarchy. */ // public static void enumerateCell(Cell root, VarContext context, // Netlist netlist, Visitor visitor) { // if (netlist == null) netlist = NetworkTool.getNetlist(root); // (new HierarchyEnumerator()).doIt(root, context, netlist, visitor, false, false, false, false); // } public static void enumerateCell(Cell root, VarContext context, Visitor visitor) { enumerateCell(root, context, visitor, Netlist.ShortResistors.NO); } public static void enumerateCell(Cell root, VarContext context, Visitor visitor, Netlist.ShortResistors shortResistors) { enumerateCell(root.getNetlist(shortResistors), context, visitor); } public static void enumerateCell(Netlist rootNetlist, VarContext context, Visitor visitor) { enumerateCell(rootNetlist, context, visitor, false); } /** Experimental. Optionally caches results of variable evaluation. */ public static void enumerateCell(Netlist rootNetlist, VarContext context, Visitor visitor, boolean caching) { Netlist.ShortResistors shortResistors = rootNetlist.getShortResistors(); (new HierarchyEnumerator()).doIt(rootNetlist.getCell(), context, rootNetlist, visitor, caching); } /** * Method to count number of unique cells in hierarchy. Useful * for progress tracking of hierarchical netlisters and writers. */ public static int getNumUniqueChildCells(Cell cell) { HashMap<Cell, Cell> uniqueChildCells = new HashMap<Cell, Cell>(); hierCellsRecurse(cell, uniqueChildCells); return uniqueChildCells.size(); } /** Recursive method used to traverse down hierarchy */ private static void hierCellsRecurse(Cell cell, HashMap<Cell, Cell> uniqueCells) { EDatabase database = cell.getDatabase(); for (Iterator<CellUsage> uit = cell.getUsagesIn(); uit.hasNext();) { CellUsage u = uit.next(); Cell subCell = u.getProto(database); if (subCell.isIcon()) { continue; } uniqueCells.put(subCell, subCell); hierCellsRecurse(subCell, uniqueCells); } } /** * Get the Network in the childNodable that corresponds to the Network in the childNodable's * parent cell. * @param parentNet the network in the parent * @param childNodable the child nodable. * @return the network in the child that connects to the network in the parent, or * null if no such network. */ public static Network getNetworkInChild(Network parentNet, Nodable childNodable) { if (childNodable == null || parentNet == null) { return null; } if (!childNodable.isCellInstance()) { return null; } Cell childCell = (Cell) childNodable.getProto(); Netlist parentNetlist = parentNet.getNetlist(); Netlist childNetlist = parentNetlist.getNetlist(childNodable); PortProto pp = null; int i = 0; boolean found = false; NodeInst ni = childNodable.getNodeInst(); // find port and index on nodable that is connected to parentNet for (Iterator<PortInst> it = ni.getPortInsts(); it.hasNext();) { PortInst pi = it.next(); pp = pi.getPortProto(); for (i = 0; i < pp.getNameKey().busWidth(); i++) { Network net = parentNetlist.getNetwork(childNodable, pp, i); // Network net = childNodable.getParent().getNetlist().getNetwork(childNodable, pp, i); if (net == parentNet) { found = true; break; } } if (found) { break; } } if (!found) { return null; } // find corresponding export in child if (childCell.contentsView() != null) { childCell = childCell.contentsView(); } Export export = childCell.findExport(pp.getNameKey()); Network childNet = childNetlist.getNetwork(export, i); // Network childNet = childCell.getNetlist().getNetwork(export, i); return childNet; } /** * Method to search if child network is connected to visitor network (visitorNet). * Used in Quick.java and Connection.java. */ public static boolean searchNetworkInParent(Network net, CellInfo info, Network visitorNet) { if (visitorNet == net) { return true; } CellInfo cinfo = info; while (net != null && cinfo.getParentInst() != null) { net = cinfo.getNetworkInParent(net); if (visitorNet == net) { return true; } cinfo = cinfo.getParentInfo(); } return false; } public static boolean searchInExportNetwork(Network net, CellInfo info, Network visitorNet) { boolean found = false; for (Iterator<Export> it = net.getExports(); !found && it.hasNext();) { Export exp = it.next(); Network tmpNet = info.getNetlist().getNetwork(exp, 0); found = searchNetworkInParent(tmpNet, info, visitorNet); } return found; } private static Network getRootNetwork(Network net, CellInfo info) { CellInfo cinfo = info; Network result = net; while (net != null && cinfo.getParentInst() != null) { net = cinfo.getNetworkInParent(net); if (net != null) { result = net; } cinfo = cinfo.getParentInfo(); } return result; } // Looking for a method to determine if two Geometrics belong to the same network public static boolean areGeometricsInSameNetwork(Geometric geo1, Network n1, Geometric geo2, Network n2, Cell top) { if (n1 == n2) { return true; // easy } // Parent networks at Cell top NetworkHierarchy search1 = new NetworkHierarchy(geo1, n1); enumerateCell(top, VarContext.globalContext, search1); NetworkHierarchy search2 = new NetworkHierarchy(geo2, n2); enumerateCell(top, VarContext.globalContext, search2); return search1.topNetwork != null && search2.topNetwork != null && search1.topNetwork == search2.topNetwork; } private static class NetworkHierarchy extends HierarchyEnumerator.Visitor { Geometric theGeo; Network childNet; Network topNetwork; NetworkHierarchy(Geometric geo, Network childN) { theGeo = geo; childNet = childN; topNetwork = null; } public boolean enterCell(HierarchyEnumerator.CellInfo info) { Cell cell = info.getCell(); // Shall I look for name first? faster? // Checking only arcs for (Iterator<ArcInst> it = cell.getArcs(); it.hasNext();) { ArcInst ai = it.next(); if (ai == theGeo) { topNetwork = getRootNetwork(childNet, info); return false; // stop looking } } return true; // keep looking? } public void exitCell(HierarchyEnumerator.CellInfo info) { } public boolean visitNodeInst(Nodable no, HierarchyEnumerator.CellInfo info) { NodeInst ni = no.getNodeInst(); if (ni.isCellInstance()) { return true; // not interested in cells. Keep looking } if (Generic.isSpecialGenericNode(ni)) { return false; // like center or pin. Stop looking } if (ni == theGeo) // found { topNetwork = getRootNetwork(childNet, info); } return false; // no need of going down since only PrimitiveNodes reach this point. } } }