/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: CompareList.java
*
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
*
* 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.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Cell.CellGroup;
import com.sun.electric.tool.Job;
/** A CompareList is a collection of Cells that are
* SUPPOSED to be topologically identical. NCC's job is
* to make
* sure that they are, in fact, topologically identical.
*
* Most of the time
* the Cells in a CompareList belong to the same Cell group.
* However, NCC's
* "joinGroup" annotation allows the designer to tell NCC to
* compare Cells that, for practical reasons, can't be placed into
* an Electric CellGroup.
*
* Note that NCC needs a VarContext for a
* Cell in order to evaluate variables such as transistor width.
* Therefore, the CompareList is actually
* a list of CellContexts to compare. Also, each CompareList has a
* boolean that says whether it's safe to compare sizes in addition to
* topologies. */
public class CompareList implements Iterable<CellContext> {
private final List<CellContext> cellContexts = new ArrayList<CellContext>();
private boolean safeToCheckSizes;
private boolean hasSkipAnnotation(List<CellContext> cellCtxts) {
for (CellContext cc : cellCtxts) {
Cell c = cc.cell;
NccCellAnnotations ann = NccCellAnnotations.getAnnotations(c);
if (ann==null) continue;
String reason = ann.getSkipReason();
if (reason!=null) {
System.out.println("Not checking: "+NccUtils.fullName(c)+
" because it has a \"skipNCC\" annotation "+
"with the comment: "+reason);
return true;
}
}
return false;
}
/** Very Subtle: If the exact same Cell is used by both designs being
* compared (for example if you're comparing 2 schematics) then
* cellContextsInGroup will contain two CellContexts for that Cell, each
* with a different context. If there are the only 2 objects in
* cellContextsInGroup (ie these 2) then this is desireable because NCC
* will compare the two CellContexts thereby creating the SubcircuitInfo
* necessary to treat the Cell hierarchically for higher level comparisons.
*
* <p>However, if cellContextsInGroup is larger than two then the two
* CellContexts that refer to the same Cell are unnecessary and can, in
* fact, be harmful. For example, if cellContextsInGroup contained
* {A, B, B} then NCC might compare {A, B} and {A, B}. The second
* comparison is redundant. Worse, it confuses a lower layer of software
* which panics when it tries to create a second Subcircuit model for B. */
private void purgeUnnecessaryDuplicateCells(List<CellContext> compareList) {
Set<Cell> cells = new HashSet<Cell>();
for (Iterator<CellContext> it=compareList.iterator();
it.hasNext() && compareList.size()>2;) {
Cell c = (it.next()).cell;
if (cells.contains(c)) it.remove();
else cells.add(c);
}
}
private boolean safeToCompareSizes(List<CellContext> cellContexts, CellUsage use1, CellUsage use2) {
for (CellContext cc : cellContexts) {
Cell c = cc.cell;
if (c.isSchematic()) {
if (use1.cellIsUsed(c) && !use1.cellHasOnlyOneSize(c)) return false;
if (use2.cellIsUsed(c) && !use2.cellHasOnlyOneSize(c)) return false;
}
}
return true;
}
/** Collect all Cells in cell's CellGroup that are used by our designs.
* These must be compared.
* <p>Since Java-Electric's CellGroups can't span libraries,
* simulate the addition of Cells (e.g. from other libraries) to a
* CellGroup using the joinGroup annotation.
*
* <p>Tricky: If a Cell is used in two layouts or two schematics then
* that Cell will occur twice in returned List, each with a
* different VarContext. This has advantages and pitfalls.
*/
public CompareList(Cell cell, CellUsage use1, CellUsage use2,
Set<CellGroup> visitedGroups) {
NccCellAnnotations ann = NccCellAnnotations.getAnnotations(cell);
Cell.CellGroup group = cell.getCellGroup();
// if a Cell is moving to another group then explore that group
// instead of the Cell's current group
if (ann!=null && ann.getGroupToJoin()!=null) group = ann.getGroupToJoin();
// joinGroups may cause us to visit a group more than once
if (visitedGroups.contains(group)) return;
visitedGroups.add(group);
// make sure we have at least one cell from use1 and one from use2
boolean used1=false, used2=false;
// add Cells with "joinGroup" annotations
Set<CellContext> compareSet = new HashSet<CellContext>();
Set<CellContext> additions = use1.getGroupAdditions(group);
if (!additions.isEmpty()) {used1=true; compareSet.addAll(additions);}
additions = use2.getGroupAdditions(group);
if (!additions.isEmpty()) {used2=true; compareSet.addAll(additions);}
// add all Cells that are actually used
for (Iterator<Cell> gi=group.getCells(); gi.hasNext();) {
Cell c = gi.next();
// A cell with a joinGroup is not in this group
NccCellAnnotations a = NccCellAnnotations.getAnnotations(c);
if (a!=null && a.getGroupToJoin()!=null && a.getGroupToJoin()!=group)
continue;
if (use1.cellIsUsed(c)) {used1=true; compareSet.add(use1.getCellContext(c));}
if (use2.cellIsUsed(c)) {used2=true; compareSet.add(use2.getCellContext(c));}
// if c is root of one design, add root of other design
if (c==use1.getRoot()) {
compareSet.add(use2.getCellContext(use2.getRoot()));
used1 = used2 = true;
}
if (c==use2.getRoot()) {
compareSet.add(use1.getCellContext(use1.getRoot()));
used1 = used2 = true;
}
}
Job.error(compareSet.size()==0, "Cell not in its own group?");
// make sure we have at least one cell from use1 and one from use2
if (!(used1 && used2)) return;
cellContexts.addAll(compareSet);
purgeUnnecessaryDuplicateCells(cellContexts);
if (hasSkipAnnotation(cellContexts)) cellContexts.clear();
safeToCheckSizes = safeToCompareSizes(cellContexts, use1, use2);
Job.error(compareSet.size()<2, "nothing to compare?");
}
/** printCells is useful for debugging
*/
public void printCells() {
System.out.print("Compare List contains:=");
for (Iterator<CellContext> it=iterator(); it.hasNext();) {
CellContext cc = it.next();
System.out.print(" "+cc.cell.getName());
}
System.out.println();
}
/** Get all the CellContexts in this CompareList.
* @return an iterator over all the CellContexts.
*/
public Iterator<CellContext> iterator() {return cellContexts.iterator();}
/** Say whether or not there are any CellContexts in CompareList.
* @return true if there is nothing in CompareList
*/
public boolean empty() {return cellContexts.size()==0;}
/** Say whether or not transistors sizes can be accurately determined
* for this CompareList. If there are multiple instances of Cells
* in this CompareList and the transistor sizes are different
* for each instance then it is NOT safe.
* @return true if we can safely check transistor sizes.
*/
public boolean isSafeToCheckSizes() {return safeToCheckSizes;}
}