/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: CellUsage.java
*
* Copyright (c) 2003 Sun Microsystems and 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.ncc.basic;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.HierarchyEnumerator;
import com.sun.electric.database.hierarchy.Nodable;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.tool.logicaleffort.LENetlister;
import com.sun.electric.tool.ncc.netlist.NccNetlist;
import com.sun.electric.tool.Job;
/** Find all Cells used in the design. Collect information about Cell usage. */
class CellUsage extends HierarchyEnumerator.Visitor {
// map from Cell to CellContext
private Map<Cell,CellContext> cellsInUse = new HashMap<Cell,CellContext>();
private List<Cell> cellsInRevTopoOrder = new ArrayList<Cell>();
private Map<Cell.CellGroup,Set<CellContext>> groupToAdditions = new HashMap<Cell.CellGroup,Set<CellContext>>();
private Cell root;
// Cells that have exactly one instantiation
private Set<Cell> singleUseCells;
// Cells with at least one LEGATE descendent
private Set<Cell> cellsWithLeGates;
private void prln(String s) {System.out.println(s);}
private void processCellGroupAdditions(CellContext cellCtxt) {
NccCellAnnotations ann = NccCellAnnotations.getAnnotations(cellCtxt.cell);
if (ann==null) return;
Cell.CellGroup group = ann.getGroupToJoin();
if (group==null) return;
Set<CellContext> additions = groupToAdditions.get(group);
if (additions==null) {
additions = new HashSet<CellContext>();
groupToAdditions.put(group, additions);
}
additions.add(cellCtxt);
}
@Override
public boolean enterCell(HierarchyEnumerator.CellInfo info) {
Cell cell = info.getCell();
if (root==null) root = cell;
VarContext context = info.getContext();
if (cellsInUse.containsKey(cell)) return false;
CellContext cellCtxt = new CellContext(cell, context);
cellsInUse.put(cell, cellCtxt);
processCellGroupAdditions(cellCtxt);
return true;
}
@Override
public void exitCell(HierarchyEnumerator.CellInfo info) {
cellsInRevTopoOrder.add(info.getCell());
}
@Override
public boolean visitNodeInst(Nodable no, HierarchyEnumerator.CellInfo info) {
return true;
}
private void addUse(Set<Cell> usedOnce, Set<Cell> usedMoreThanOnce, Cell c) {
if (usedMoreThanOnce.contains(c)) return;
if (usedOnce.contains(c)) {
usedOnce.remove(c);
usedMoreThanOnce.add(c);
} else {
usedOnce.add(c);
}
}
private Set<Cell> findSingleUseCells() {
Set<Cell> singleUseCells = new HashSet<Cell>();
Map<Cell,Set<Cell>> cellToUsedOnce = new HashMap<Cell,Set<Cell>>();
Set<Cell> usedMoreThanOnce = new HashSet<Cell>();
// For each Cell in the design: c in reverse topological order
for (Iterator<Cell> it=cellsInReverseTopologicalOrder(); it.hasNext();) {
Cell c = it.next();
// usedOnce holds all Cells instantiated exactly once by the design hierarchy
// rooted at c
Set<Cell> usedOnce = new HashSet<Cell>();
// For each child Cell of c: child
for (Iterator<Nodable> noIt=c.getNetlist(NccNetlist.SHORT_RESISTORS).getNodables(); noIt.hasNext();) {
NodeProto np = noIt.next().getProto();
if (!(np instanceof Cell)) continue;
Cell child = (Cell) np;
addUse(usedOnce, usedMoreThanOnce, child);
// Subtle: If child is an icon without a schematic then child
// won't have usedOnce set. Apparently Nodables aren't created
// for icons with no schematic.
if (!cellToUsedOnce.containsKey(child)) continue;
Set<Cell> childUsedOnce = cellToUsedOnce.get(child);
// For each descendent of child instantiated exactly once by child
for (Cell ci : childUsedOnce) {
addUse(usedOnce, usedMoreThanOnce, ci);
}
}
cellToUsedOnce.put(c, usedOnce);
if (!it.hasNext()) {
// Cell c is the root. Create set of all cells
// in the design that are instantiated exactly once.
singleUseCells.add(c);
singleUseCells.addAll(usedOnce);
}
}
return singleUseCells;
}
// I copied these tests from Spice.java. However, they
// don't feel right to me.
// I think the Cell should be marked LEGATE and not the instance. RKao
private boolean isLeGate(Nodable no) {
// The following two lines don't work because too many NodeInsts
// of non-LE-Gates have the LEGATE attribute. This causes NCC
// to flatten too much when size checking is turned on. This causes
// NCC's hierarchy heuristics to behave badly.
// return no.getVar(LENetlister.ATTR_LEGATE)!=null ||
// no.getVar(LENetlister.ATTR_LEKEEPER)!=null;
boolean attrLeGate = no.getParameterOrVariable(LENetlister.ATTR_LEGATE)!=null ||
no.getParameterOrVariable(LENetlister.ATTR_LEKEEPER)!=null;
boolean hasGetDrive = false;
for (Iterator<Variable> vIt=no.getDefinedParameters(); vIt.hasNext();) {
// for (Iterator<Variable> vIt=no.getVariables(); vIt.hasNext();) {
Variable v = vIt.next();
if (v.isJava()) {
String expr = v.getObject().toString();
// It's spelled two different ways
if (expr.indexOf("getDrive")!=-1 || expr.indexOf("getdrive")!=-1) {
hasGetDrive = true;
}
}
}
if (hasGetDrive && !attrLeGate) {
prln(" Warning: instance: "+no.getName()+" in Cell: "+
no.getParent().describe(false)+
" has a variable that calls getDrive but the instance "+
"has no variable named LEGATE or LEKEEPER");
}
if (attrLeGate && !hasGetDrive) {
// This is no longer illegal. In the new standard cell
// logical effort methodology
// an LEGATE indicates that the LE tool should substitute a
// standard cell of the appropriate size.
// prln(" Warning: instance: "+no.getName()+" in Cell: "+
// no.getParent().describe(false)+
// " has a variable named LEGATE or LEKEEPER but has"+
// " no variable that calls getDrive()");
}
return hasGetDrive;
}
// find Cells that have at least one LEGATE descendent
private Set<Cell> findCellsWithLeGate() {
Set<Cell> cellsWithLeGate = new HashSet<Cell>();
for (Iterator<Cell> it=cellsInReverseTopologicalOrder(); it.hasNext();) {
Cell c = it.next();
boolean hasLeGate = false;
for (Iterator<Nodable> noIt=c.getNetlist(NccNetlist.SHORT_RESISTORS).getNodables(); noIt.hasNext();) {
Nodable no = noIt.next();
if (isLeGate(no)) {hasLeGate=true; break;}
NodeProto np = no.getProto();
if (!(np instanceof Cell)) continue;
Cell child = (Cell) np;
if (cellsWithLeGate.contains(child)) {hasLeGate=true; break;}
}
if (hasLeGate) {
// // Debug
System.out.println(" Cell contains LE gate: "+c.describe(false));
cellsWithLeGate.add(c);
}
}
return cellsWithLeGate;
}
private boolean cellIsParameterized(Cell c) {
return c.hasParameters();
}
private void postProcess() {
singleUseCells = findSingleUseCells();
cellsWithLeGates = findCellsWithLeGate();
}
private CellUsage() {}
// ------------------ Here's the real interface ------------------------
// static constructor
/** Scan the design hierarchy rooted at root and construct a list of
* all Cells instantiated by that design.
* @param root the root of the design
*/
public static CellUsage getCellUsage(CellContext root) {
CellUsage visitor = new CellUsage();
HierarchyEnumerator.enumerateCell(root.cell, root.context, visitor);
visitor.postProcess();
return visitor;
}
/** See if a particular Cell is used by the design.
* @param cell the Cell to look for.
* @return true if Cell occurs in the design
*/
public boolean cellIsUsed(Cell cell) {return cellsInUse.containsKey(cell);}
/** Cell has only one size in the design */
public boolean cellHasOnlyOneSize(Cell cell) {
return singleUseCells.contains(cell) ||
(!cellsWithLeGates.contains(cell)) && !cellIsParameterized(cell);
}
/** Get an iterator over a list of all the cells used in the design. The
* list is sorted in reverse topological order.
* @return the iterator over the list
*/
public Iterator<Cell> cellsInReverseTopologicalOrder() {
return cellsInRevTopoOrder.iterator();
}
/** Get a CellContext for Cell cell. If the Cell is instantiated multiple
* times then arbitrarily select one instance and return the CellContext
* for that instance.
* @param cell the cell for which we want the CellContext
* @return a CellContext for the instance of Cell cell
*/
public CellContext getCellContext(Cell cell) {
Job.error(!cellsInUse.containsKey(cell), "cell not found");
return cellsInUse.get(cell);
}
/** A joinGroup annotation indicates that NCC should treat a Cell
* as belonging to a particular Electric CellGroup. The method
* getGroupAdditions passes along information from joinGroup
* annotations.
* @return a Set of CellContexts to add to group */
public Set<CellContext> getGroupAdditions(Cell.CellGroup group) {
Set<CellContext> additions = groupToAdditions.get(group);
return additions!=null ? additions : new HashSet<CellContext>();
}
/** Get the root Cell of the design */
public Cell getRoot() {return root;}
}