/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: MergeSerialParallel.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.processing;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import com.sun.electric.tool.ncc.NccGlobals;
import com.sun.electric.tool.ncc.netlist.Mos;
import com.sun.electric.tool.ncc.netlist.NetObject;
import com.sun.electric.tool.ncc.netlist.Part;
import com.sun.electric.tool.ncc.netlist.Subcircuit;
import com.sun.electric.tool.ncc.netlist.Wire;
import com.sun.electric.tool.ncc.trees.Circuit;
import com.sun.electric.tool.ncc.trees.EquivRecord;
/**
* Merge parallel Transistors into one. Merge stacks of Transistors into
* one stacked Transistor. <p>
*
* Tricky: The process of merging requires the deletion of Parts and
* Wires. My first attempt to handle deletion efficiently was to use Sets to
* hold Parts and Wires. However, this was bad because it used too much
* storage. My new approach is to mark Parts and Wires "deleted" and later
* in a single pass remove "deleted" objects from lists after all merging
* is complete. Note that "deleted" Parts and Wires should never exist before
* or after serial parallel merge.<p>
*
* More Tricky:
* This phase gets executed before Wires get put in cannonical form. That
* means we must be prepared for a Part to occur more than once on a Wire's
* parts list. We must also be prepared to enounter "deleted" parts. One
* implication is that I can't check the number of parts on a wire by
* simply calling Wire.getNumParts() because I need to add extra code to
* exclude deleted and duplicate Parts!
*/
public class SerialParallelMerge {
private NccGlobals globals;
private SerialParallelMerge(NccGlobals globals) {this.globals=globals;}
private boolean serialMerge() {
int numMerged = 0;
EquivRecord wires = globals.getWires();
if (wires!=null) {
// don't blow up if no wires
for (Iterator<Circuit> it=wires.getCircuits(); it.hasNext();) {
Circuit ckt = it.next();
for (Iterator<NetObject> ni=ckt.getNetObjs(); ni.hasNext();) {
Wire w = (Wire) ni.next();
if (Mos.joinOnWire(w, globals.getOptions())) numMerged++;
}
}
}
globals.status2(" Serial merged "+numMerged+" Transistors");
return numMerged>0;
}
/** parallelMergeAllCandidatesInList attempts to parallel merge the Parts in
* parts. Because there is a possibility of hash code collisions,
* I examine all n^2 combinations to guarantee that all possible
* parallel merges are performed.
* @param parts Collection of Parts to merge in parallel
* @return the count of Parts actually merged */
private int parallelMergeAllCandidatesInSet(Collection<Part> parts) {
int numMerged = 0;
// Linked list allows O(1) remove()
LinkedList<Part> pts = new LinkedList<Part>(parts);
while (true) {
Iterator<Part> it= pts.iterator();
if (!it.hasNext()) break;
Part first= it.next();
it.remove();
while (it.hasNext()) {
Part p = it.next();
if (first.parallelMerge(p, globals.getOptions())) {
it.remove();
numMerged++;
}
}
}
return numMerged;
}
private int parallelMergeEachSetInMap(Map<Integer,Set<Part>> map) {
int numMerged = 0;
for(Integer i : map.keySet()){
Set<Part> j= map.get(i);
numMerged += parallelMergeAllCandidatesInSet(j);
}
return numMerged;
}
// process candidate transistors from one Wire
// Tricky: Set removes duplicate Parts on Wire.parts list
private int parallelMergePartsOnWire(Wire w) {
if (w.isDeleted()) return 0;
HashMap<Integer,Set<Part>> map = new HashMap<Integer,Set<Part>>();
for (Iterator<Part> it=w.getParts(); it.hasNext();) {
Part p = it.next();
if (p.isDeleted()) continue;
// we don't parallel merge Subcircuits
if (p instanceof Subcircuit) continue;
Integer code = p.hashCodeForParallelMerge();
Set<Part> set = map.get(code);
if (set==null) {
set= new HashSet<Part>();
map.put(code,set);
}
set.add(p);
}
return parallelMergeEachSetInMap(map);
}
private boolean parallelMerge() {
int numMerged = 0;
EquivRecord er = globals.getWires();
if (er!=null) {
// don't blow up if no wires
for (Iterator<Circuit> it=er.getCircuits(); it.hasNext();) {
Circuit ckt = it.next();
for (Iterator<NetObject> ni=ckt.getNetObjs(); ni.hasNext();) {
Wire w = (Wire)ni.next();
numMerged += parallelMergePartsOnWire(w);
}
}
}
globals.status2(" Parallel merged " + numMerged + " Parts");
return numMerged>0;
}
private int countUndeletedParts(EquivRecord parts) {
int numParts = 0;
for (Iterator<Circuit> it=parts.getCircuits(); it.hasNext();) {
Circuit ckt = it.next();
numParts += ckt.numUndeletedNetObjs();
}
return numParts;
}
// merge the various parts using series and parallel lists
private void serialParallelMerge() {
EquivRecord parts = globals.getParts();
if (parts==null) return; // No Cell has Parts
int numParts = countUndeletedParts(parts);
globals.status2("--- NCC starting merge process with " +
numParts + " Parts");
// Tricky: Don't give up if the first parallel merge attempt fails because
// the following first serial merge may succeed!
boolean first = true;
for (int tripNumber=1; ; tripNumber++) {
if (globals.userWantsToAbort()) break;
globals.status2(" parallel and series merge trip " + tripNumber);
boolean progress = parallelMerge();
if (!first && !progress) break;
first = false;
if (globals.userWantsToAbort()) break;
progress = serialMerge();
if (!progress) break;
}
numParts = countUndeletedParts(parts);
globals.status2("--- NCC finishing merge process with " +
numParts + " Parts");
globals.status2("");
}
private static void putInFinalForm(EquivRecord er) {
if (er==null) return;
for (Iterator<Circuit> it=er.getCircuits(); it.hasNext();) {
it.next().putInFinalForm();
}
}
public static void doYourJob(NccGlobals globals){
SerialParallelMerge sp = new SerialParallelMerge(globals);
sp.serialParallelMerge();
putInFinalForm(globals.getParts());
putInFinalForm(globals.getWires());
}
}