/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: Analysis.java
*
* Copyright (c) 2004 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.
*/
package com.sun.electric.tool.simulation;
import com.sun.electric.database.text.TextUtils;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
/**
* Class to define a set of simulation data.
* This class encapsulates all of the simulation data that is displayed in a waveform window.
* It includes the labels and values.
* It can handle digital, analog, and many variations (intervals, sweeps).
*/
public abstract class Analysis<S extends Signal>
{
public static class AnalysisType
{
private String name;
private static List<AnalysisType> allTypes = new ArrayList<AnalysisType>();
AnalysisType(String name)
{
this.name = name;
allTypes.add(this);
}
public String toString() { return name; }
public static AnalysisType findAnalysisType(String analysisName)
{
for(AnalysisType at : allTypes)
{
if (at.name.equals(analysisName)) return at;
}
return null;
}
}
/** indicates general signals */ public static final AnalysisType ANALYSIS_SIGNALS = new AnalysisType("Signals");
/** indicates transient analysis */ public static final AnalysisType ANALYSIS_TRANS = new AnalysisType("Transient");
/** indicates AC analysis */ public static final AnalysisType ANALYSIS_AC = new AnalysisType("AC");
/** indicates DC analysis */ public static final AnalysisType ANALYSIS_DC = new AnalysisType("DC");
/** indicates Measurement data */ public static final AnalysisType ANALYSIS_MEAS = new AnalysisType("Measurement");
/** the Stimuli in which this Analysis resides */ private Stimuli sd;
/** the type of analysis data here */ private AnalysisType type;
/** a list of all signals in this Analysis */ private List<S> signals = new ArrayList<S>();
/** a map of all signal names in this Analysis */ private HashMap<String,S> signalNames = new HashMap<String,S>();
/** the range of values in this Analysis */ private Rectangle2D bounds;
/** the left and right side of the Analysis */ private double leftEdge, rightEdge;
/** true to extrapolate last value in waveform window */ private boolean extrapolateToRight;
/** group of signals from extracted netlist of same net */ private HashMap<String,List<S>> signalGroup = new HashMap<String,List<S>>();
/**
* Constructor for a collection of simulation data.
* @param sd Stimuli that this analysis is part of.
* @param type the type of this analysis.
* @param extrapolateToRight true to draw the last value to the right
* (useful for IRSIM and other digital simulations).
* False to stop drawing signals after their last value
* (useful for Spice and other analog simulations).
*/
public Analysis(Stimuli sd, AnalysisType type, boolean extrapolateToRight)
{
this.sd = sd;
this.type = type;
this.extrapolateToRight = extrapolateToRight;
sd.addAnalysis(this);
}
/**
* Free allocated resources before closing.
*/
public void finished()
{
for (S s : signals)
s.finished();
signals.clear();
signalNames.clear();
}
/**
* Method to return the Stimuli in which this Analysis resides.
* @return the Stimuli in which this Analysis resides.
*/
public Stimuli getStimuli()
{
return sd;
}
/**
* Method to return the type of data currently being manipulated.
* Possibilities are ANALYSIS_TRANS, ANALYSIS_AC, ANALYSIS_DC, or ANALYSIS_MEAS.
* @return the type of data currently being manipulated.
*/
public AnalysisType getAnalysisType()
{
return type;
}
/**
* Method to tell whether signal values should be extrapolated to the
* right side of the waveform window.
* @return true to draw the last value to the right (useful for IRSIM and
* other digital simulations). False to stop drawing signals after their
* last value (useful for Spice and other analog simulations).
*/
public boolean extrapolateValues()
{
return extrapolateToRight;
}
public boolean isUseLegacySimulationCode()
{
return sd.isUseLegacySimulationCode();
}
/**
* Method to get the list of signals in this Simulation Data object.
* @return a List of signals.
*/
public List<S> getSignals() { return signals; }
public void nameSignal(S ws, String sigName)
{
String name = TextUtils.canonicString(sigName);
signalNames.put(name, ws);
// simulators may strip off last "_"
if (name.indexOf('_') >= 0 && !name.endsWith("_"))
signalNames.put(name + "_", ws);
// keep track of groups of signals that represent one extracted net
String baseName = getBaseNameFromExtractedNet(name, sd.getNetDelimiter());
List<S> sigs = signalGroup.get(baseName);
if (sigs == null) {
sigs = new ArrayList<S>();
signalGroup.put(baseName, sigs);
}
sigs.add(ws);
}
/**
* Method to add a new signal to this Simulation Data object.
* Signals can be either digital or analog.
* @param ws the signal to add.
* Instead of a "Signal", use either DigitalSignal or AnalogSignal.
*/
public void addSignal(S ws)
{
signals.add(ws);
String sigName = ws.getFullName();
if (sigName != null) nameSignal(ws, sigName);
setBoundsDirty();
}
private static String getBaseNameFromExtractedNet(String signalFullName, String delim) {
// String delim = Simulation.getSpiceExtractedNetDelimiter();
int hashPos = signalFullName.indexOf(delim);
if (hashPos > 0)
{
return signalFullName.substring(0, hashPos);
} else {
return signalFullName;
}
}
/**
* Get a list of signals that are from the same network.
* Extracted nets are the original name + delimiter + some junk
* @param ws the signal
* @return a list of signals
*/
public List<S> getSignalsFromExtractedNet(Signal ws) {
String sigName = ws.getFullName();
if (sigName == null) return new ArrayList<S>();
sigName = TextUtils.canonicString(sigName);
sigName = getBaseNameFromExtractedNet(sigName, sd.getNetDelimiter());
return signalGroup.get(sigName);
}
/**
* Method to compute the time and value bounds of this simulation data.
* @return a Rectangle2D that has time bounds in the X part and
* value bounds in the Y part.
*/
public Rectangle2D getBounds()
{
if (bounds == null)
{
bounds = null;
for(Signal sig : signals)
{
Rectangle2D sigBounds = sig.getBounds();
if (bounds == null)
{
bounds = new Rectangle2D.Double(sigBounds.getMinX(), sigBounds.getMinY(), sigBounds.getWidth(), sigBounds.getHeight());
leftEdge = sig.getLeftEdge();
rightEdge = sig.getRightEdge();
} else
{
Rectangle2D.union(bounds, sigBounds, bounds);
if (leftEdge < rightEdge)
{
leftEdge = Math.min(leftEdge, sig.getLeftEdge());
rightEdge = Math.max(rightEdge, sig.getRightEdge());
} else
{
// backwards time values
leftEdge = Math.max(leftEdge, sig.getLeftEdge());
rightEdge = Math.min(rightEdge, sig.getRightEdge());
}
}
}
}
return bounds;
}
/**
* Method to return the leftmost X coordinate of this Analysis.
* This value may not be the same as the minimum-x of the bounds, because
* the data may not be monotonically increasing (may run backwards, for example).
* @return the leftmost X coordinate of this Analysis.
*/
public double getLeftEdge()
{
getBounds();
return leftEdge;
}
/**
* Method to return the rightmost X coordinate of this Analysis.
* This value may not be the same as the maximum-x of the bounds, because
* the data may not be monotonically increasing (may run backwards, for example).
* @return the rightmost X coordinate of this Analysis.
*/
public double getRightEdge()
{
getBounds();
return rightEdge;
}
public void setBoundsDirty() { bounds = null; }
/**
* Method to tell whether this simulation data is analog or digital.
* @return true if this simulation data is analog.
*/
public abstract boolean isAnalog();
/**
* Method to quickly return the signal that corresponds to a given Network name.
* Not all names may be found (because of name mangling, which this method does not handle).
* But the lookup is faster than "findSignalForNetwork".
* @param netName the Network name to find.
* @return the Signal that corresponds with the Network.
* Returns null if none can be found.
*/
public S findSignalForNetworkQuickly(String netName)
{
String lookupName = TextUtils.canonicString(netName);
S sSig = signalNames.get(lookupName);
return sSig;
}
/**
* Method to return the signal that corresponds to a given Network name.
* @param netName the Network name to find.
* @return the Signal that corresponds with the Network.
* Returns null if none can be found.
*/
public S findSignalForNetwork(String netName)
{
// look at all signal names in the cell
for(Iterator<S> it = getSignals().iterator(); it.hasNext(); )
{
S sSig = it.next();
String signalName = sSig.getFullName();
if (netName.equalsIgnoreCase(signalName)) return sSig;
// if the signal name has underscores, see if all alphabetic characters match
if (signalName.length() + 1 == netName.length() && netName.charAt(signalName.length()) == ']')
{
signalName += "_";
}
if (signalName.length() == netName.length() && signalName.indexOf('_') >= 0)
{
boolean matches = true;
for(int i=0; i<signalName.length(); i++)
{
char sigChar = signalName.charAt(i);
char netChar = netName.charAt(i);
if (TextUtils.isLetterOrDigit(sigChar) != TextUtils.isLetterOrDigit(netChar))
{
matches = false;
break;
}
if (TextUtils.isLetterOrDigit(sigChar) &&
TextUtils.canonicChar(sigChar) != TextUtils.canonicChar(netChar))
{
matches = false;
break;
}
}
if (matches) return sSig;
}
}
return null;
}
}