/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: Stimuli.java
*
* Copyright (c) 2004, 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.simulation;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.tool.user.waveform.WaveformWindow;
import com.sun.electric.util.TextUtils;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* This class represents a set of simulation *inputs* -- that is,
* "force" and "release" events -- in contrast to Signal, which
* represents simulation *outputs*.
*/
public class Stimuli
{
// logic levels and signal strengths for digital signals
public static final int LOGIC = 03;
public static final int LOGIC_LOW = 0;
public static final int LOGIC_X = 1;
public static final int LOGIC_HIGH = 2;
public static final int LOGIC_Z = 3;
public static final int STRENGTH = 014;
public static final int OFF_STRENGTH = 0;
public static final int NODE_STRENGTH = 04;
public static final int GATE_STRENGTH = 010;
public static final int VDD_STRENGTH = 014;
/** the WaveformWindow associated with this Stimuli */ private WaveformWindow ww;
/** the simulation engine associated with this Stimuli */ private Engine engine;
/** the cell attached to this Stimuli information */ private Cell cell;
/** the disk file associated with this Stimuli */ private URL fileURL;
/** the separator character that breaks names */ private char separatorChar;
/** the SignalCollections in this Stimuli */ private Map<String,SignalCollection> scMap;
/** the list of SignalCollections in this Stimuli */ private List<SignalCollection> scList;
/** control points when signals are selected */ private Map<Signal<?>,Double[]> controlPointMap;
/** Cached version of net delimiter**/ private String delim;
/**
* Constructor to build a new Simulation Data object.
*/
public Stimuli()
{
separatorChar = '.';
scMap = new HashMap<String,SignalCollection>();
scList = new ArrayList<SignalCollection>();
controlPointMap = new HashMap<Signal<?>,Double[]>();
delim = " ";
}
/**
* Free allocated resources before closing.
*/
public void finished()
{
controlPointMap.clear();
scMap.clear();
}
public void addSignalCollection(SignalCollection an)
{
scMap.put(an.getName(), an);
scList.add(an);
}
/**
* Method to find a SignalCollection with a given name.
* @param type the stimulus type being queried.
* @return the SignalCollection with that name (null if not found).
*/
public SignalCollection findSignalCollection(String title)
{
SignalCollection an = scMap.get(title);
return an;
}
public void setNetDelimiter(String d) { delim = d; }
public String getNetDelimiter() { return delim; }
public Iterator<SignalCollection> getSignalCollections() { return scList.iterator(); }
/**
* Method to set the Cell associated with this simulation data.
* The associated Cell is the top-level cell in the hierarchy,
* and is usually the Cell that was used to generate the simulation input deck.
* @param cell the Cell associated with this simulation data.
*/
public void setCell(Cell cell) { this.cell = cell; }
/**
* Method to return the Cell associated with this simulation data.
* The associated Cell is the top-level cell in the hierarchy,
* and is usually the Cell that was used to generate the simulation input deck.
* @return the Cell associated with this simulation data.
*/
public Cell getCell() { return cell; }
/**
* Method to set the simulation Engine associated with this simulation data.
* This is only for data associated with built-in simulators (ALS and IRSIM).
* @param engine the simulation Engine associated with this simulation data.
*/
public void setEngine(Engine engine) { this.engine = engine; }
/**
* Method to return the simulation Engine associated with this simulation data.
* This is only for data associated with built-in simulators (ALS and IRSIM).
* @return the simulation Engine associated with this simulation data.
*/
public Engine getEngine() { return engine; }
public void setWaveformWindow(WaveformWindow ww) { this.ww = ww; }
/**
* Method to return the separator character for names in this simulation.
* The separator character separates levels of hierarchy. It is usually a "."
* @return the separator character for names in this simulation.
*/
public char getSeparatorChar() { return separatorChar; }
/**
* Method to set the separator character for names in this simulation.
* The separator character separates levels of hierarchy. It is usually a "."
* @param sep the separator character for names in this simulation.
*/
public void setSeparatorChar(char sep) { separatorChar = sep; }
/**
* Method to set a URL to the file containing this simulation data.
* @param fileURL a URL to the file containing this simulation data.
*/
public void setFileURL(URL fileURL) { this.fileURL = fileURL; }
/**
* Method to return a URL to the file containing this simulation data.
* @return a URL to the file containing this simulation data.
*/
public URL getFileURL() { return fileURL; }
/**
* Method to return the WaveformWindow that displays this simulation data.
* @return the WaveformWindow that displays this simulation data.
*/
public WaveformWindow getWaveformWindow() { return ww; }
/**
* Method to return an array of control points associated with a signal.
* Control points are places where the user has added stimuli to the signal (set a level or strength).
* These points can be selected for change of the stimuli.
* @param sig the signal in question.
* @return an array of times where there are control points.
* Null if no control points are defined.
*/
public Double [] getControlPoints(Signal<?> sig) { return controlPointMap.get(sig); }
/**
* Method to clear the list of control points associated with a signal.
* Control points are places where the user has added stimuli to the signal (set a level or strength).
* These points can be selected for change of the stimuli.
* @param sig the signal to clear.
*/
public void clearControlPoints(Signal<?> sig) { controlPointMap.remove(sig); }
/**
* Method to add a new control point to the list on a signal.
* Control points are places where the user has added stimuli to the signal (set a level or strength).
* These points can be selected for change of the stimuli.
* @param sig the signal in question.
* @param time the time of the new control point.
*/
public void addControlPoint(Signal<?> sig, double time)
{
Double [] controlPoints = controlPointMap.get(sig);
if (controlPoints == null)
{
controlPoints = new Double[1];
controlPoints[0] = new Double(time);
controlPointMap.put(sig, controlPoints);
} else
{
// see if it is in the list already
for(int i=0; i<controlPoints.length; i++)
if (controlPoints[i].doubleValue() == time) return;
// extend the list
Double [] newCP = new Double[controlPoints.length + 1];
for(int i=0; i<controlPoints.length; i++)
newCP[i] = controlPoints[i];
newCP[controlPoints.length] = new Double(time);
controlPointMap.put(sig, newCP);
}
}
/**
* Method to remove control points the list on a signal.
* Control points are places where the user has added stimuli to the signal (set a level or strength).
* These points can be selected for change of the stimuli.
* @param sig the signal in question.
* @param time the time of the control point to delete.
*/
public void removeControlPoint(Signal<?> sig, double time)
{
Double [] controlPoints = controlPointMap.get(sig);
if (controlPoints == null) return;
// see if it is in the list already
boolean found = false;
for(int i=0; i<controlPoints.length; i++)
if (controlPoints[i].doubleValue() == time) { found = true; break; }
if (!found) return;
// shrink the list
Double [] newCP = new Double[controlPoints.length - 1];
int j = 0;
for(int i=0; i<controlPoints.length; i++)
{
if (controlPoints[i].doubleValue() != time)
newCP[j++] = controlPoints[i];
}
controlPointMap.put(sig, newCP);
}
// /**
// * 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()
// {
// // determine extent of the data
// Rectangle2D bounds = null;
// for(SignalCollection sc : scList)
// {
// Rectangle2D scBounds = sc.getBounds();
// if (scBounds == null) continue;
// if (bounds == null)
// {
// bounds = new Rectangle2D.Double(scBounds.getMinX(), scBounds.getMinY(), scBounds.getWidth(), scBounds.getHeight());
// } else
// {
// Rectangle2D.union(bounds, scBounds, bounds);
// }
// }
// return bounds;
// }
/**
* Method to return the leftmost X coordinate of this Stimuli.
* 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 Stimuli.
*/
public double getMinTime()
{
double leftEdge = 0, rightEdge = 0;
for(SignalCollection sc : scList)
{
for (Signal<?> sig : sc.getSignals())
{
if (leftEdge == rightEdge)
{
leftEdge = sig.getMinTime();
rightEdge = sig.getMaxTime();
} else
{
if (leftEdge < rightEdge)
{
leftEdge = Math.min(leftEdge, sig.getMinTime());
rightEdge = Math.max(rightEdge, sig.getMaxTime());
} else
{
leftEdge = Math.max(leftEdge, sig.getMinTime());
rightEdge = Math.min(rightEdge, sig.getMaxTime());
}
}
}
}
return leftEdge;
}
/**
* Method to return the rightmost X coordinate of this Stimuli.
* 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 Stimuli.
*/
public double getMaxTime()
{
double leftEdge = 0, rightEdge = 0;
for(SignalCollection sc : scList)
{
for (Signal<?> sig : sc.getSignals())
{
if (leftEdge == rightEdge)
{
leftEdge = sig.getMinTime();
rightEdge = sig.getMaxTime();
} else
{
if (leftEdge < rightEdge)
{
leftEdge = Math.min(leftEdge, sig.getMinTime());
rightEdge = Math.max(rightEdge, sig.getMaxTime());
} else
{
leftEdge = Math.max(leftEdge, sig.getMinTime());
rightEdge = Math.min(rightEdge, sig.getMaxTime());
}
}
}
}
return rightEdge;
}
/**
* Method to tell whether this simulation data is analog or digital.
* @return true if this simulation data is analog.
*/
public boolean isAnalog()
{
for(SignalCollection sc : scList)
{
for (Signal<?> sig : sc.getSignals())
{
if (!sig.isDigital()) return true;
}
}
return false;
}
/**
* Method to convert a strength to an index value.
* The strengths are OFF_STRENGTH, NODE_STRENGTH, GATE_STRENGTH, and VDD_STRENGTH.
* The indices are integers that can be saved to disk.
* @param strength strength level.
* @return the index for that strength (0-based).
*/
public static int strengthToIndex(int strength) { return strength / 4; }
/**
* Method to convert a strength index to a strength value.
* The strengths are OFF_STRENGTH, NODE_STRENGTH, GATE_STRENGTH, and VDD_STRENGTH.
* The indices of the strengths are integers that can be saved to disk.
* @param index a strength index (0-based).
* @return the equivalent strength.
*/
public static int indexToStrength(int index) { return index * 4; }
/**
* Method to describe the level in a given state.
* A 'state' is a combination of a level and a strength.
* The levels are LOGIC_LOW, LOGIC_HIGH, LOGIC_X, and LOGIC_Z.
* @param state the given state.
* @return a description of the logic level in that state.
*/
public static String describeLevel(int state)
{
switch (state&Stimuli.LOGIC)
{
case Stimuli.LOGIC_LOW: return "low";
case Stimuli.LOGIC_HIGH: return "high";
case Stimuli.LOGIC_X: return "undefined";
case Stimuli.LOGIC_Z: return "floating";
}
return "?";
}
/**
* Method to describe the level in a given state, with only 1 character.
* A 'state' is a combination of a level and a strength.
* The levels are LOGIC_LOW, LOGIC_HIGH, LOGIC_X, and LOGIC_Z.
* @param state the given state.
* @return a description of the logic level in that state.
*/
public static String describeLevelBriefly(int state)
{
switch (state&Stimuli.LOGIC)
{
case Stimuli.LOGIC_LOW: return "L";
case Stimuli.LOGIC_HIGH: return "H";
case Stimuli.LOGIC_X: return "X";
case Stimuli.LOGIC_Z: return "Z";
}
return "?";
}
/**
* Method to convert a state representation (L, H, X, Z) to a state
* @param s1 character string that contains state value.
* @return the state value.
*/
public static int parseLevel(String s1)
{
if (s1.length() > 0)
{
switch (s1.charAt(0))
{
case 'L': case 'l': return Stimuli.LOGIC_LOW;
case 'X': case 'x': return Stimuli.LOGIC_X;
case 'H': case 'h': return Stimuli.LOGIC_HIGH;
case 'Z': case 'z': return Stimuli.LOGIC_Z;
}
}
return Stimuli.LOGIC_X;
}
/**
* Method to describe the strength in a given state.
* A 'state' is a combination of a level and a strength.
* The strengths are OFF_STRENGTH, NODE_STRENGTH, GATE_STRENGTH, and VDD_STRENGTH.
* @param strength the given strength.
* @return a description of the strength in that state.
*/
public static String describeStrength(int strength)
{
switch (strength&Stimuli.STRENGTH)
{
case Stimuli.OFF_STRENGTH: return "off";
case Stimuli.NODE_STRENGTH: return "node";
case Stimuli.GATE_STRENGTH: return "gate";
case Stimuli.VDD_STRENGTH: return "power";
}
return "?";
}
public static SignalCollection newSignalCollection(Stimuli sd, final String title)
{
SignalCollection sc = new SignalCollection(title);
sd.addSignalCollection(sc);
return sc;
}
/**
* Method to find busses in a list of signals and create them.
* @param curArray the list of signals.
* @param sc the SignalCollection in which the signals reside.
*/
public void makeBusSignals(List<Signal<?>> signalList, SignalCollection sc)
{
if (signalList == null) return;
Collections.sort(signalList, new WaveformWindow.SignalsByName());
List<Signal<?>> busSoFar = new ArrayList<Signal<?>>();
for(Signal<?> sig : signalList)
{
String curSignalName = sig.getSignalName();
int squarePos = curSignalName.indexOf('[');
if (squarePos < 0)
{
makeBus(busSoFar, sc);
continue;
}
boolean startNewBus = false;
if (busSoFar.size() > 0)
{
String curBusName = curSignalName.substring(0, squarePos);
int curIndex = TextUtils.atoi(curSignalName.substring(squarePos+1));
String curScope = sig.getSignalContext();
if (curScope == null) curScope = "";
Signal<?> lastSig = busSoFar.get(busSoFar.size()-1);
String lastSignalName = lastSig.getSignalName();
squarePos = lastSignalName.indexOf('[');
String lastBusName = lastSignalName.substring(0, squarePos);
int lastIndex = TextUtils.atoi(lastSignalName.substring(squarePos+1));
String lastScope = lastSig.getSignalContext();
if (lastScope == null) lastScope = "";
if (!lastBusName.equals(curBusName)) startNewBus = true; else
if (!lastScope.equals(curScope)) startNewBus = true; else
if (lastIndex+1 != curIndex) startNewBus = true;
}
if (startNewBus)
makeBus(busSoFar, sc);
busSoFar.add(sig);
}
makeBus(busSoFar, sc);
}
private void makeBus(List<Signal<?>> busSoFar, SignalCollection sc)
{
if (busSoFar.size() == 0) return;
int width = busSoFar.size();
Signal<DigitalSample>[] subsigs = (Signal<DigitalSample>[])new Signal[width];
for(int i=0; i<width; i++)
subsigs[i] = (Signal<DigitalSample>)busSoFar.get(i);
// get first index
String firstEntryName = subsigs[0].getSignalName();
int firstSquarePos = firstEntryName.indexOf('[');
int firstIndex = TextUtils.atoi(firstEntryName.substring(firstSquarePos+1));
// get last index
String lastEntryName = subsigs[width-1].getSignalName();
int lastSquarePos = lastEntryName.indexOf('[');
int lastIndex = TextUtils.atoi(lastEntryName.substring(lastSquarePos+1));
// make the bus
String busName = firstEntryName.substring(0, firstSquarePos) + "[" + firstIndex + ":" + lastIndex + "]";
String scope = subsigs[0].getSignalContext();
BusSample.createSignal(sc, this, busName, scope, true, subsigs);
// reset the list
busSoFar.clear();
}
}