/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: LocalPartitioning.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.
*/
/**
* This is the second generation algorithm for local partitioning of Wires.
* It's intended to be more efficient. (That is not O(n^2) in number of
* Port instances.)
*/
package com.sun.electric.tool.ncc.processing;
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;
import java.util.Set;
import com.sun.electric.database.text.TextUtils;
import com.sun.electric.tool.ncc.NccGlobals;
import com.sun.electric.tool.ncc.lists.LeafList;
import com.sun.electric.tool.ncc.netlist.NetObject;
import com.sun.electric.tool.ncc.netlist.Part;
import com.sun.electric.tool.ncc.netlist.PinType;
import com.sun.electric.tool.ncc.netlist.Wire;
import com.sun.electric.tool.ncc.strategy.Strategy;
import com.sun.electric.tool.ncc.trees.EquivRecord;
import com.sun.electric.tool.Job;
/**
* Partition Wires into equivalence classes based only upon
* local information. Compute a Signature for each wire and then
* group together Wires with the same Signature.
*/
public class LocalPartitionWires {
// ------------------------ data ---------------------------------
private final NccGlobals globals;
// ------------------------ classes ---------------------------------
private static class PinTypeCount {
private PinType pinType;
private int count;
@Override
public boolean equals(Object o) {
if (!(o instanceof PinTypeCount)) return false;
PinTypeCount p = (PinTypeCount) o;
return p.count==count && p.pinType==pinType;
}
@Override
public int hashCode() {
return pinType.hashCode() * count;
}
public PinTypeCount(PinType t) {pinType=t;}
public void increment() {count++;}
public String description() {
return count+" "+pinType.description();
}
}
// Sort PinTypeCounts by PinType name
private static class PinTypeCompare implements Comparator<PinTypeCount> {
public int compare(PinTypeCount c1, PinTypeCount c2) {
String n1 = c1.pinType.description();
String n2 = c2.pinType.description();
return TextUtils.STRING_NUMBER_ORDER.compare(n1, n2);
}
}
/** A Wire's Signature is an unordered list of of the pairs {PinType, count}.
* For example, 2 Mos diffusions and 1 Mos gate. Signatures are used as
* hash keys to quickly partition Wires based upon their Signatures.
*/
public static class Signature {
private Map<PinType,PinTypeCount> pinTypeToPinTypeCount = new HashMap<PinType,PinTypeCount>();
// Note: id isn't supposed to be used by equals()
private Integer id;
private List<PinTypeCount> getListOfPinTypeCounts() {
List<PinTypeCount> l = new ArrayList<PinTypeCount>();
for (PinType t : pinTypeToPinTypeCount.keySet()) {
PinTypeCount c = pinTypeToPinTypeCount.get(t);
l.add(c);
}
return l;
}
@Override
public int hashCode() {
int code = 0;
for (PinType t : pinTypeToPinTypeCount.keySet()) {
PinTypeCount c = pinTypeToPinTypeCount.get(t);
code += t.hashCode() + c.hashCode();
}
return code;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Signature)) return false;
Signature s2 = (Signature) o;
if (pinTypeToPinTypeCount.size()!=s2.pinTypeToPinTypeCount.size())
return false;
for (PinType t : pinTypeToPinTypeCount.keySet()) {
PinTypeCount c = pinTypeToPinTypeCount.get(t);
PinTypeCount c2 = s2.pinTypeToPinTypeCount.get(t);
if (!c.equals(c2)) return false;
}
return true;
}
public void increment(PinType t) {
PinTypeCount c = pinTypeToPinTypeCount.get(t);
if (c==null) {
c = new PinTypeCount(t);
pinTypeToPinTypeCount.put(t, c);
}
c.increment();
}
public void setID(int id) {
Job.error(this.id!=null, "assigned a second ID?");
this.id = new Integer(id);
}
public Integer getID() {return id;}
public List<String> getReasons() {
List<PinTypeCount> pinTypeCounts = getListOfPinTypeCounts();
Collections.sort(pinTypeCounts, new PinTypeCompare());
List<String> pinTypeDescrs = new ArrayList<String>();
if (pinTypeCounts.size()==0) {
pinTypeDescrs.add("disconnected");
} else {
for (PinTypeCount t : pinTypeCounts) {
String descr = t.description();
pinTypeDescrs.add(descr);
}
}
return pinTypeDescrs;
}
}
private static class ComputeSignatures extends Strategy {
private Map<Wire,Signature> wireToSignature = new HashMap<Wire,Signature>();
private Signature disconnectedWireSignature = new Signature();
// Set signatures for Wires with nothing attached
private void doFor(Wire w) {
if (w.numParts()==0) {
wireToSignature.put(w, disconnectedWireSignature);
}
}
// Compute signatures for all Wires attached to PortInsts
private void doFor(Part p) {
int pinNdx=0;
for (Iterator<Wire> itw=p.getConnected(); itw.hasNext(); pinNdx++) {
Wire w = itw.next();
PinType t = p.getPinTypeOfNthPin(pinNdx);
Signature s = wireToSignature.get(w);
if (s==null) {
s = new Signature();
wireToSignature.put(w, s);
}
s.increment(t);
}
}
@Override
public Integer doFor(NetObject no) {
if (no instanceof Part) {
doFor((Part)no);
} else if (no instanceof Wire) {
doFor((Wire)no);
}
return Strategy.CODE_NO_CHANGE;
}
private ComputeSignatures(NccGlobals globs) {super(globs);}
private Map<Wire,Signature> doYourJob2() {
EquivRecord root = globals.getRoot();
// don't blow up if no Parts Wires or Ports
if (root!=null) doFor(root);
return wireToSignature;
}
// Calculate a signature for each Wire
public static Map<Wire,Signature> doYourJob(NccGlobals globs) {
return (new ComputeSignatures(globs)).doYourJob2();
}
}
// If two Signatures are != but are .equals() then discard one of them.
// Assign each remaining Signature a unique Integer ID.
private void cannonizeSignatures(Map<Wire,Signature> wireToSignature) {
Map<Signature,Signature> signatures = new HashMap<Signature,Signature>();
int sigID = 0;
for (Wire w : wireToSignature.keySet()) {
Signature s = wireToSignature.get(w);
Signature cannonicalS = signatures.get(s);
if (cannonicalS==null) {
cannonicalS = s;
signatures.put(cannonicalS, cannonicalS);
cannonicalS.setID(sigID++);
} else {
wireToSignature.put(w, cannonicalS);
}
}
}
private static class StratLocalPartitionWires extends Strategy {
private Map<Wire,Signature> wireToSignature;
private Set<Wire> forcedMatchWires;
private StratLocalPartitionWires(Map<Wire,Signature> wireToSignature,
Set<Wire> forcedMatchWires, NccGlobals globals) {
super(globals);
this.wireToSignature = wireToSignature;
this.forcedMatchWires = forcedMatchWires;
}
@Override
public Integer doFor(NetObject o) {
Wire w = (Wire) o;
if (forcedMatchWires.contains(w)) {
// don't repartition Wires that were forced to match
return 0;
} else {
Signature s = wireToSignature.get(w);
// add 1000 to avoid collision with 0 which means "don't partition"
return 1000 + s.getID();
}
}
private void summary(LeafList offspring) {
globals.status2("StratLocalPartitionWires produced " + offspring.size() +
" offspring");
if (offspring.size()!=0) {
globals.status2(offspring.sizeInfoString());
globals.status2(offspringStats(offspring));
}
}
private void doYourJob2(EquivRecord wireRec) {
LeafList offspring = doFor(wireRec);
summary(offspring);
}
public static void doYourJob(Map<Wire,Signature> wireToSignature,
Set<Wire> forcedMatchWires, NccGlobals globs) {
(new StratLocalPartitionWires(wireToSignature, forcedMatchWires, globs))
.doYourJob2(globs.getWires());
}
}
// Add a signature to each Wire partition
private static class SignPartitions extends Strategy {
private Map<Wire,Signature> wireToSignature;
// Pass signature from doFor(NetObject) to doFor(EquivRecord)
private Signature lastSignature;
@Override
public LeafList doFor(EquivRecord er) {
LeafList l = super.doFor(er);
if (er.isLeaf()) er.setWireSignature(lastSignature);
return l;
}
@Override
public Integer doFor(NetObject no) {
lastSignature = wireToSignature.get(no);
return Strategy.CODE_NO_CHANGE;
}
private SignPartitions(Map<Wire,Signature> wireToSignature, NccGlobals globs) {
super(globs);
this.wireToSignature = wireToSignature;
}
public static void doYourJob(Map<Wire,Signature> wireToSignature, NccGlobals globs) {
(new SignPartitions(wireToSignature, globs))
.doFor(globs.getWires());
}
}
private LocalPartitionWires(NccGlobals globs) {globals=globs;}
private void doYourJob(Set<Wire> forcedMatchWires) {
EquivRecord wires = globals.getWires();
if (wires==null) return; // don't blow up if no Wires
Map<Wire,Signature> wireToSignature = ComputeSignatures.doYourJob(globals);
cannonizeSignatures(wireToSignature);
StratLocalPartitionWires.doYourJob(wireToSignature, forcedMatchWires, globals);
SignPartitions.doYourJob(wireToSignature, globals);
}
// ------------------------ public method ---------------------------------
/** Partition Wires based upon purely local information
* @param forcedMatchWires Wires that were pre-matched by the user
* @param globs variables used by all of NCC
*/
public static void doYourJob(Set<Wire> forcedMatchWires, NccGlobals globs) {
globs.status2("Partition Wires using local information");
LocalPartitionWires lpw = new LocalPartitionWires(globs);
lpw.doYourJob(forcedMatchWires);
}
}