/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: NccEngine.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.
*/
/**
* NccEngine performs an n-way netlist comparison. NccEngine
* must be passed all Ncc options as procedural arguments; it does not access
* the User NCC options directly. This allows programs to call the
* engine without becoming involved with how User's options are stored.
*/
package com.sun.electric.tool.ncc;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.tool.ncc.basic.NccUtils;
import com.sun.electric.tool.ncc.netlist.NccNetlist;
import com.sun.electric.tool.ncc.netlist.Part;
import com.sun.electric.tool.ncc.netlist.Wire;
import com.sun.electric.tool.ncc.processing.ExportChecker;
import com.sun.electric.tool.ncc.processing.ForceMatch;
import com.sun.electric.tool.ncc.processing.HashCodePartitioning;
import com.sun.electric.tool.ncc.processing.HierarchyInfo;
import com.sun.electric.tool.ncc.processing.LocalPartitionResult;
import com.sun.electric.tool.ncc.processing.LocalPartitioning;
import com.sun.electric.tool.ncc.processing.ReportHashCodeFailure;
import com.sun.electric.tool.ncc.processing.SerialParallelMerge;
import com.sun.electric.tool.ncc.result.NccResult;
import com.sun.electric.tool.ncc.result.BenchmarkResults.BenchIdx;
import com.sun.electric.tool.ncc.strategy.StratCheckSizes;
import com.sun.electric.tool.ncc.trees.Circuit;
import com.sun.electric.tool.ncc.trees.EquivRecord;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/** NccEngine performs the topological comparison of two Cells.
* These Cells are from the same
* CompareList and are supposed to be topologically
* identical.
*
* Note that NCC calls NccEngine.compare() many times. For example,
* if every CompareList contained exactly two Cells, then NCC
* would call NccEngine.compare() once for each CompareList.
*
* NCC calls NccEngine.compare() on the leaf Cells first, and proceeds
* upwards towards the
* root. The object, HierarchyInfo, stores information that NCC
* must pass from lower level comparisons to higher level comparisons.
*/
public class NccEngine {
// ------------------------------ private data ----------------------------
private NccGlobals globals;
// ----------------------- private methods --------------------------------
// return null if user aborts
private List<NccNetlist> buildNccNetlists(List<Cell> cells,
List<VarContext> contexts,
boolean blackBox,
HierarchyInfo hierInfo) {
globals.error(cells.size()!=contexts.size(),
"number of cells, and contexts must be the same");
List<NccNetlist> nccLists = new ArrayList<NccNetlist>();
Iterator<Cell> itCell;
Iterator<VarContext> itCon;
for (itCell=cells.iterator(),
itCon=contexts.iterator(); itCell.hasNext();) {
Cell cell = itCell.next();
VarContext context = itCon.next();
NccNetlist nccList = new NccNetlist(cell, context,
hierInfo, blackBox, globals);
if (nccList.userAbort()) return null;
nccLists.add(nccList);
}
return nccLists;
}
private int[] getNetObjCounts(EquivRecord rec, int numCells) {
int[] counts = new int[numCells];
// don't blow up if no parts or wires
if (rec==null) return counts;
int i=0;
for (Iterator<Circuit> it=rec.getCircuits(); it.hasNext(); i++) {
Circuit ckt = it.next();
counts[i] = ckt.numNetObjs();
}
return counts;
}
/** Ivan wants this print out. */
private void printWireComponentCounts() {
int numCells = globals.getNumNetlistsBeingCompared();
int[] partCounts = getNetObjCounts(globals.getParts(), numCells);
int[] wireCounts = getNetObjCounts(globals.getWires(), numCells);
int[] portCounts = getNetObjCounts(globals.getWires(), numCells);
String[] cellNames = globals.getRootCellNames();
VarContext[] contexts = globals.getRootContexts();
for (int i=0; i<cellNames.length; i++) {
globals.status1(
" Cell: "+cellNames[i]+" has "+wireCounts[i]+" wires, "+
partCounts[i]+" parts, and "+portCounts[i]+
" ports after series/parallel combination. "+
"Instance path: "+
contexts[i].getInstPath("/")
);
}
}
private NccResult designsMatch(HierarchyInfo hierInfo,
boolean hierInfoOnly) {
long start = NccUtils.getTime();
boolean noNetlists = globals.cantBuildNetlist();
if (noNetlists) {
// Can't build some net list. This is a mismatch.
globals.status2("netlist error");
// Preserve the invariant: "part, wire, and port LeafLists exist"
// even if there are no Parts and no Wires OR there is a netlist error.
globals.initLeafLists();
return NccResult.newResult(false, false, false, globals);
} else {
// Date d0 = new Date();
ExportChecker expCheck = new ExportChecker(globals);
expCheck.markPortsForRenaming();
boolean expNamesOK = expCheck.matchByName();
if (expNamesOK) {
expCheck.saveInfoNeededToMakeMeASubcircuit(hierInfo);
} else {
if (hierInfo!=null) hierInfo.purgeCurrentCompareList();
}
if (globals.getRoot()==null) {
// all Cells contain no Wires and no Parts. The cells match but the
// rest of NCC isn't going to be happy. We'll have to exit early.
globals.status2("all cells empty");
// Preserve the invariant: "part, wire, and port LeafLists exist"
// even if there are no Parts and no Wires OR there is a netlist error.
globals.initLeafLists();
return NccResult.newResult(true, true, true, globals);
}
// Useless so far
//expCheck.printExportTypeWarnings();
start = NccUtils.registerTiming(" Export name matching took: ",start,BenchIdx.EXPORT_MATCHING_TIME,globals);
// Date d1 = new Date();
// globals.status1(" Export name matching took: "+
// NccUtils.hourMinSec(d0, d1));
if (globals.userWantsToAbort()) return NccResult.newUserAbortResult();
SerialParallelMerge.doYourJob(globals);
start = NccUtils.registerTiming(" Serial/parallel merge took: ",start,BenchIdx.MERGE_TIME,globals);
// Date d2 = new Date();
// globals.status1(" Serial/parallel merge took: "+
// NccUtils.hourMinSec(d1, d2));
if (globals.userWantsToAbort()) return NccResult.newUserAbortResult();
printWireComponentCounts();
// We need to keep track of forcedParts and forcedWires because we
// mustn't repartition EquivRecords containing Parts and Wires that
// we explicitly forced to match!!!
Set<Part> forcedParts = new HashSet<Part>();
Set<Wire> forcedWires = new HashSet<Wire>();
ForceMatch.doYourJob(forcedParts, forcedWires, globals);
LocalPartitionResult localRes =
LocalPartitioning.doYourJob(forcedParts, forcedWires, globals);
if (globals.userWantsToAbort()) return NccResult.newUserAbortResult();
// Tricky: init leaf lists after Local Partitioning because Local
// Partitioning can make an EquivRecord change from matched to
// mismatched!
globals.initLeafLists();
start = NccUtils.registerTiming(" Local partitioning took ",start,BenchIdx.LOCAL_PARTITIONING_TIME,globals);
// Date d3 = new Date();
// globals.status1(" Local partitioning took "+
// NccUtils.hourMinSec(d2, d3));
boolean topoOK = HashCodePartitioning.doYourJob(globals);
if (!localRes.matches()) {
globals.getNccGuiInfo().setPartRecReports(localRes.getPartRecReports());
globals.getNccGuiInfo().setWireRecReports(localRes.getWireRecReports());
localRes.printErrorReport();
return NccResult.newResult(expNamesOK, false, false, globals);
}
if (globals.userWantsToAbort()) return NccResult.newUserAbortResult();
//Date d4 = new Date();
start = NccUtils.getTime();
boolean expTopoOK = true;
if (topoOK) {
expCheck.suggestPortMatchesBasedOnTopology();
expTopoOK = expCheck.ensureExportsWithMatchingNamesAreOnEquivalentNets();
}
start = NccUtils.registerTiming(" Export checking took ",start,BenchIdx.EXPORT_CHECKING_TIME,globals);
// Date d5 = new Date();
// globals.status1(" Export checking took "+NccUtils.hourMinSec(d4, d5));
boolean sizesOK = StratCheckSizes.doYourJob(globals);
start = NccUtils.registerTiming(" Size checking took ",start,BenchIdx.SIZE_CHECKING_TIME,globals);
// Date d6 = new Date();
// globals.status1(" Size checking took "+NccUtils.hourMinSec(d5, d6));
if (!topoOK) {
ReportHashCodeFailure hcf = new ReportHashCodeFailure(globals);
globals.getNccGuiInfo().setPartRecReports(hcf.getPartRecReports());
globals.getNccGuiInfo().setWireRecReports(hcf.getWireRecReports());
}
boolean exportsOK = expNamesOK && expTopoOK;
boolean topologyOK = localRes.matches() && topoOK;
return NccResult.newResult(exportsOK, topologyOK, sizesOK, globals);
}
}
private NccResult areEquivalent(List<Cell> cells, List<VarContext> contexts,
HierarchyInfo hierInfo,
boolean blackBox,
NccOptions options, Aborter aborter) {
globals = new NccGlobals(options, aborter);
globals.status2("****************************************"+
"****************************************");
// black boxing is implemented by building netlists that are empty
// except for their Exports.
Date before = new Date();
List<NccNetlist> nccNetlists =
buildNccNetlists(cells, contexts, blackBox, hierInfo);
Date after = new Date();
globals.status1(" NCC net list construction took "+NccUtils.hourMinSec(before, after)+".");
// null list returned means user requested abort
if (nccNetlists==null) return NccResult.newUserAbortResult();
globals.setInitialNetlists(nccNetlists);
NccResult result = designsMatch(hierInfo, false);
if(result.match())
NccUtils.incrementBenchmarkCount(BenchIdx.PASS_RESULT, globals);
else{
NccUtils.incrementBenchmarkCount(BenchIdx.FAIL_RESULT, globals);
}
globals.status2("****************************************"+
"****************************************");
return result;
}
private static NccResult compare2(Cell cell1, VarContext context1,
Cell cell2, VarContext context2,
HierarchyInfo hierInfo,
boolean blackBox,
NccOptions options,
Aborter aborter) {
ArrayList<Cell> cells = new ArrayList<Cell>();
cells.add(cell1);
cells.add(cell2);
ArrayList<VarContext> contexts = new ArrayList<VarContext>();
contexts.add(context1);
contexts.add(context2);
return compareMany(cells, contexts, hierInfo, blackBox,
options, aborter);
}
private static NccResult compareMany(List<Cell> cells, List<VarContext> contexts,
HierarchyInfo hierCompInfo,
boolean blackBox,
NccOptions options, Aborter aborter) {
NccEngine ncc = new NccEngine();
return ncc.areEquivalent(cells, contexts, hierCompInfo,
blackBox, options, aborter);
}
// -------------------------- public methods ------------------------------
/**
* Check to see if all cells are topologically equivalent. Note that
* the NCC engine can compare any number of Cells at the same time.
* @param cells a list of cells to compare.
* @param contexts a list of VarContexts for the corresponding Cell. The
* VarContxt is used to evaluate schematic
* variables. Use null if variables don't need to be evaluated.
* @param hierCompInfo Information needed to perform hierarchical
* netlist comparison. For flat comparisons pass null.
* @param options NCC options
*/
public static NccResult compare(List<Cell> cells, List<VarContext> contexts,
HierarchyInfo hierCompInfo,
NccOptions options, Aborter aborter) {
return compareMany(cells, contexts, hierCompInfo, false,
options, aborter);
}
/**
* Check to see if two cells are topologically equivalent.
* @param cell1 the root cell for the first design
* @param context1 the VarContext for the first Cell
* @param cell2 the root cell for the second design
* @param context2 the VarContext for the second Cell
* @param hierInfo Information needed to perform hierarchical
* netlist comparison. For flat comparisons pass null.
* @param options NCC options
*/
public static NccResult compare(Cell cell1, VarContext context1,
Cell cell2, VarContext context2,
HierarchyInfo hierInfo,
NccOptions options, Aborter aborter) {
return compare2(cell1, context1, cell2, context2, hierInfo, false,
options, aborter);
}
/**
* Instead of checking to see if two cells are topologically
* equivalent, simply pretend that we've already compared them
* and found them to match. Put information into hierInfo that indicates
* that these two Cells match.
* @param cell1 the root cell for the first design
* @param ctxt1 the VarContext for the first Cell
* @param cell2 the root cell for the second design
* @param ctxt2 the VarContext for the second Cell
* @param hierInfo Information needed to perform hierarchical
* netlist comparison. For flat comparisons pass null.
* @param options NCC options
*/
public static NccResult buildBlackBoxes(Cell cell1, VarContext ctxt1,
Cell cell2, VarContext ctxt2,
HierarchyInfo hierInfo,
NccOptions options, Aborter aborter) {
return compare2(cell1, ctxt1, cell2, ctxt2, hierInfo, true,
options, aborter);
}
}