/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: HSpiceOut.java * Input/output tool: reader for HSpice output (tr, pa, ac, sw, mt) * Written by Steven M. Rubin, Sun Microsystems. * * 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.io.input; import com.sun.electric.database.hierarchy.Cell; import com.sun.electric.tool.simulation.ComplexSample; import com.sun.electric.tool.simulation.MutableSignal; import com.sun.electric.tool.simulation.ScalarSample; import com.sun.electric.tool.simulation.Signal; import com.sun.electric.tool.simulation.SignalCollection; import com.sun.electric.tool.simulation.Stimuli; import com.sun.electric.tool.simulation.SweptSample; import com.sun.electric.util.TextUtils; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; /** * Class for reading and displaying waveforms from HSpice output. * This includes transient information in .tr and .pa files (.pa0/.tr0, .pa1/.tr1, ...) * It also includes AC analysis in .ac files; DC analysis in .sw files; * and Measurements in .mt files. * * While trying to debug the condition count handling, these test cases were observed: * CASE VERSION ANALYSIS NUMNOI SWEEPCNT CNDCNT CONDITIONS * H01 9007 TR 0 4 1 bma_w * H02 9007 TR 36 19 1 sweepv * H03 9007 TR 2 30 1 MONTE_CARLO * DC 258 30 1 MONTE_CARLO * H04 9007 TR 2 7 1 TEMPERATURE * DC 0 0 0 * AC 0 6 1 bigcap * H05 9601 TR 0 0 0 * H06 9601 TR 0 0 0 * H07 9601 TR 2 25 3 data_tim, inbufstr, outloadstr (sweep header has 2 numbers) * H08 9601 TR 4 3 2 ccdata, cc * AC 4 2 1 lpvar (***CRASHES***) * H09 9601 TR 0 3 3 rdata, r, c (sweep header has 2 numbers) * AC 0 3 3 rdata, r, c (sweep header has 2 numbers) * H10 9601 TR 0 4 8 rdata, r0, r1, r2, r3, r4, c0, c1 (sweep header has 7 numbers) * AC 0 4 8 rdata, r0, r1, r2, r3, r4, c0, c1 (sweep header has 7 numbers) */ public class HSpiceOut extends Input<Stimuli> { private static final boolean DEBUGCONDITIONS = false; /** true if tr/ac/sw file is binary */ private boolean isTRACDCBinary; /** true if binary tr/ac/sw file has bytes swapped */ private boolean isTRACDCBinarySwapped; /** the raw file base */ private String fileBase; /** the "tr" files (X.tr0, X.tr1, ...): transient */ private List<String> trFiles; /** the "sw" file extension (sw0, sw1, ...): DC */ private String swExtension; /** the "ic" file extension (ic0, ic1, ...): old DC */ private String icExtension; /** the "ac" file extension (ac0, ac1, ...): AC */ private String acExtension; /** the "mt" file extension (mt0, mt1, ...): measurement */ private String mtExtension; /** the "pa" file extension (pa0, pa1, ...): long names */ private String paExtension; /** signal names in file (may be continued in next) */ private String [] signalNames; /** signals created (may span multiple files) */ private Signal<?>[][] allSignals; /** sweep names (may span multiple files) */ private String [] sweepNames; private int binaryTRACDCSize, binaryTRACDCPosition; private boolean eofReached; private byte [] binaryTRACDCBuffer; /** * Class to hold HSpice name associations from the .paX file */ private static class PALine { int number; String string; } HSpiceOut() {} /** * Method to read HSpice output files. * @param sd Stimuli associated to the reading. * @param fileURL the URL to one of the output files. * @param cell the Cell associated with these HSpice output files. */ protected Stimuli processInput(URL fileURL, Cell cell, Stimuli sd) throws IOException { // find all ".tr" files trFiles = new ArrayList<String>(); String fileNameNoPath = TextUtils.getFileNameWithoutExtension(TextUtils.URLtoString(fileURL), true) + ".tr"; String topDirName = TextUtils.getFilePath(fileURL); File topDir = new File(topDirName); String [] fileList = topDir.list(); for(int i=0; i<fileList.length; i++) { if (!fileList[i].startsWith(fileNameNoPath)) continue; trFiles.add(topDirName + fileList[i]); } Collections.sort(trFiles, TextUtils.STRING_NUMBER_ORDER); if (trFiles.size() > 1) System.out.println("Reading " + trFiles.size() + " 'tr' files..."); // figure out other file names swExtension = "sw0"; icExtension = "ic0"; acExtension = "ac0"; mtExtension = "mt0"; paExtension = "pa0"; fileBase = fileURL.getFile(); int dotPos = fileBase.lastIndexOf('.'); if (dotPos > 0) { String extension = fileBase.substring(dotPos+1); fileBase = fileBase.substring(0, dotPos); if (extension.startsWith("sw") || extension.startsWith("ic") || extension.startsWith("ac") || extension.startsWith("mt") || extension.startsWith("pa")) { swExtension = "sw" + extension.substring(2); icExtension = "ic" + extension.substring(2); acExtension = "ac" + extension.substring(2); mtExtension = "mt" + extension.substring(2); paExtension = "pa" + extension.substring(2); } } // the .pa file has name information List<PALine> paList = readPAFile(fileURL); // read Transient analysis data (.tr file) addTRData(sd, paList, fileURL); // read DC analysis data (.sw file) addDCData(sd, paList, fileURL); // read AC analysis data (.ac file) addACData(sd, paList, fileURL); // read measurement data (.mt file) addMeasurementData(sd, fileURL); // return the simulation data return sd; } /** * Method to find the ".mt" file and read measurement data. * @param sd the Stimuli to add this measurement data to. * @param fileURL the URL to the ".tr" file. * @throws IOException */ private void addMeasurementData(Stimuli sd, URL fileURL) throws IOException { // find the associated ".mt" name file URL mtURL = null; try { mtURL = new URL(fileURL.getProtocol(), fileURL.getHost(), fileURL.getPort(), fileBase + "." + mtExtension); } catch (java.net.MalformedURLException e) { } if (mtURL == null) return; if (!TextUtils.URLExists(mtURL)) return; if (openTextInput(mtURL)) return; System.out.println("Reading HSpice measurements file: " + mtURL.getFile()); SignalCollection sc = Stimuli.newSignalCollection(sd, "MEASUREMENTS"); List<String> measurementNames = new ArrayList<String>(); HashMap<String,List<Double>> measurementData = new HashMap<String,List<Double>>(); String lastLine = null; for(;;) { // get line from file String nextLine = lastLine; if (nextLine == null) { nextLine = lineReader.readLine(); if (nextLine == null) break; } if (nextLine.startsWith("$") || nextLine.startsWith(".")) continue; String [] keywords = breakMTLine(nextLine, false); if (keywords.length == 0) break; // gather measurement names on the first time out if (measurementNames.size() == 0) { for(int i=0; i<keywords.length; i++) measurementNames.add(keywords[i]); for(;;) { lastLine = lineReader.readLine(); if (lastLine == null) break; keywords = breakMTLine(lastLine, true); if (keywords.length == 0) { lastLine = null; break; } if (TextUtils.isANumber(keywords[0])) break; for(int i=0; i<keywords.length; i++) if (keywords[i].length() > 0) measurementNames.add(keywords[i]); } for(String mName : measurementNames) { measurementData.put(mName, new ArrayList<Double>()); } continue; } // get data values int index = 0; for(int i=0; i<keywords.length; i++) { if (keywords[i].length() == 0) continue; String mName = measurementNames.get(index++); List<Double> mData = measurementData.get(mName); mData.add(new Double(TextUtils.atof(keywords[i]))); } for(;;) { if (index >= measurementNames.size()) break; lastLine = lineReader.readLine(); if (lastLine == null) break; keywords = breakMTLine(lastLine, true); if (keywords.length == 0) break; for(int i=0; i<keywords.length; i++) { if (keywords[i].length() == 0) continue; String mName = measurementNames.get(index++); List<Double> mData = measurementData.get(mName); mData.add(new Double(TextUtils.atof(keywords[i]))); } } lastLine = null; continue; } // convert this to a list of Measurements List<Double> argMeas = measurementData.get(measurementNames.get(0)); double[] time = new double[argMeas.size()]; for (int i = 0; i < argMeas.size(); i++) time[i] = argMeas.get(i).doubleValue(); List<Signal<ScalarSample>> measData = new ArrayList<Signal<ScalarSample>>(); for(String mName : measurementNames) { List<Double> mData = measurementData.get(mName); double[] values = new double[mData.size()]; for(int i=0; i<mData.size(); i++) values[i] = mData.get(i).doubleValue(); // special case with the "alter#" name...remove the "#" if (mName.equals("alter#")) mName = "alter"; Signal<ScalarSample> as = ScalarSample.createSignal(sc, sd, mName, null, time, values); measData.add(as); } closeInput(); } /** * Method to parse a line from a measurement (.mt0) file. * @param line the line from the file. * @param continuation true if the line is supposed to be a continuation * after the first line. * @return an array of strings on the line (zero-length if at end). */ private String[] breakMTLine(String line, boolean continuation) { List<String> strings = new ArrayList<String>(); for(int i=1; ; ) { if (line.length() <= i+1) break; int end = i+17; if (end > line.length()) end = line.length(); while (end < line.length() && line.charAt(end-1) != ' ') end++; String part = line.substring(i, end).trim(); // if (i == 1) // { // // first token: make sure it is blank if a continuation // if (continuation && part.length() > 0) return new String[0]; // } if (part.length() > 0) strings.add(part.trim()); i = end; } int actualSize = strings.size(); String [] retVal = new String[actualSize]; for(int i=0; i<actualSize; i++) retVal[i] = strings.get(i); return retVal; } /** * Method to find the ".tr" file and read transient data. * @param sd the Stimuli to add this transient data to. * @param fileURL the URL to the ".tr" file. * @throws IOException */ private void addTRData(Stimuli sd, List<PALine> paList, URL fileURL) throws IOException { int numFiles = trFiles.size(); for(int i=0; i<numFiles; i++) { // find the ".tr" name file String trFile = trFiles.get(i); URL trURL = null; try { trURL = new URL(fileURL.getProtocol(), fileURL.getHost(), fileURL.getPort(), trFile); } catch (java.net.MalformedURLException e) {} if (trURL == null) return; if (!TextUtils.URLExists(trURL)) return; // process the DC data readTRDCACFile(sd, trURL, paList, "TRANS SIGNALS", i, numFiles); } } /** * Method to find the ".sw" file and read DC data. * @param sd the Stimuli to add this DC data to. * @param fileURL the URL to the ".tr" file. * @throws IOException */ private void addDCData(Stimuli sd, List<PALine> paList, URL fileURL) throws IOException { // find the associated ".sw" name file URL swURL = null; try { swURL = new URL(fileURL.getProtocol(), fileURL.getHost(), fileURL.getPort(), fileBase + "." + swExtension); } catch (java.net.MalformedURLException e) {} if (swURL != null && TextUtils.URLExists(swURL)) { // process the DC data readTRDCACFile(sd, swURL, paList, "DC SIGNALS", 0, 1); return; } // no associated ".sw" file, look for an ".ic" name file URL icURL = null; try { icURL = new URL(fileURL.getProtocol(), fileURL.getHost(), fileURL.getPort(), fileBase + "." + icExtension); } catch (java.net.MalformedURLException e) {} if (icURL != null && TextUtils.URLExists(icURL)) { // can't process the DC data System.out.println("WARNING: Cannot read old DC format file (." + icExtension + ")...must provide new format (." + swExtension + "): " + fileBase + "." + icExtension); return; } } /** * Method to find the ".ac" file and read AC data. * @param sd the Stimuli to add this AC data to. * @param fileURL the URL to the ".tr" file. * @throws IOException */ private void addACData(Stimuli sd, List<PALine> paList, URL fileURL) throws IOException { // find the associated ".ac" name file URL acURL = null; try { acURL = new URL(fileURL.getProtocol(), fileURL.getHost(), fileURL.getPort(), fileBase + "." + acExtension); } catch (java.net.MalformedURLException e) {} if (acURL == null) return; if (!TextUtils.URLExists(acURL)) return; // process the AC data readTRDCACFile(sd, acURL, paList, "AC SIGNALS", 0, 1); } /** * Method to read the "pa" file with full symbol names. * These files can end in "0", "1", "2",... * @param fileURL the URL to the simulation output file * @return a list of PALine objects that describe the name mapping file entries. */ private List<PALine> readPAFile(URL fileURL) throws IOException { // find the associated ".pa" name file URL paURL = null; try { paURL = new URL(fileURL.getProtocol(), fileURL.getHost(), fileURL.getPort(), fileBase + "." + paExtension); } catch (java.net.MalformedURLException e) {} if (paURL == null) return null; if (!TextUtils.URLExists(paURL)) return null; if (openTextInput(paURL)) return null; List<PALine> paList = new ArrayList<PALine>(); for(;;) { // get line from file String nextLine = lineReader.readLine(); if (nextLine == null) break; // break into number and name String trimLine = nextLine.trim(); int spacePos = trimLine.indexOf(' '); if (spacePos > 0) { // save it in a PALine object PALine pl = new PALine(); pl.number = TextUtils.atoi(trimLine, 0, 10); pl.string = removeLeadingX(trimLine.substring(spacePos+1).trim()); paList.add(pl); } } closeInput(); return paList; } private void readTRDCACFile(Stimuli sd, URL fileURL, List<PALine> paList, String analysisTitle, int fileNum, int numFiles) throws IOException { if (openBinaryInput(fileURL)) return; eofReached = false; resetBinaryTRACDCReader(); SignalCollection sc; if (fileNum == 0) { sc = Stimuli.newSignalCollection(sd, analysisTitle); } else { sc = sd.findSignalCollection(analysisTitle); if (sc == null) return; } startProgressDialog("HSpice " + analysisTitle + " analysis", fileURL.getFile()); if (fileNum == 0) { System.out.println("Reading HSpice " + analysisTitle + " file: " + fileURL.getFile()); } else { System.out.println("Also reading file: " + fileURL.getFile()); } // get number of nodes int nodcnt = getHSpiceInt(); // switch to text reading if the file is text if (!isTRACDCBinary) { closeInput(); if (openTextInput(fileURL)) return; eofReached = false; resetBinaryTRACDCReader(); int nodcntTxt = getHSpiceInt(); if (nodcnt != nodcntTxt) System.out.println("WARNING: Binary interpretation saw " + nodcnt + " nodes but text sees " + nodcntTxt); } // get number of special items int numnoi = getHSpiceInt(); // get number of conditions int cndcnt = getHSpiceInt(); /* * Although this isn't documented anywhere, it appears that the 4th * number in the file is a multiplier for the first, which allows * there to be more than 10000 nodes. */ StringBuffer line = new StringBuffer(); for(int j=0; j<4; j++) line.append((char)getByteFromFile()); int multiplier = TextUtils.atoi(line.toString(), 0, 10); nodcnt += multiplier * 10000; int numSignals = numnoi + nodcnt - 1; if (numSignals <= 0) { System.out.println("Error reading " + fileURL.getFile()); closeInput(); stopProgressDialog(); return; } // get version number (known to work with 9007, 9601) int version = getHSpiceInt(); if (version != 9007 && version != 9601) System.out.println("Warning: may not be able to read HSpice files of type " + version); // ignore the unused/title information (4+72 characters over line break) line = new StringBuffer(); for(int j=0; j<76; j++) { int k = getByteFromFile(); line.append((char)k); if (!isTRACDCBinary && (k == '\n' || k == '\r')) j--; } // ignore the date/time information (16 characters) line = new StringBuffer(); for(int j=0; j<16; j++) line.append((char)getByteFromFile()); // ignore the copyright information (72 characters over line break) line = new StringBuffer(); for(int j=0; j<72; j++) { int k = getByteFromFile(); line.append((char)k); if (!isTRACDCBinary && (k == '\n' || k == '\r')) j--; } // get number of sweeps int sweepcnt = getHSpiceInt(); if (DEBUGCONDITIONS) System.out.println("++++++++++++++++++++ VERSION="+version+" SWEEPCNT="+sweepcnt+" CNDCNT="+cndcnt+" NUMNOI="+numnoi+" MULTIPLIER="+multiplier); if (cndcnt == 0) sweepcnt = 0; // ignore the Monte Carlo information (76 characters over line break) line = new StringBuffer(); for(int j=0; j<76; j++) { int k = getByteFromFile(); line.append((char)k); if (!isTRACDCBinary && (k == '\n' || k == '\r')) j--; } // get the type of each signal if (fileNum == 0) { signalNames = new String[numSignals]; } else { if (numSignals != signalNames.length) { System.out.println("Error: file " + fileURL.getFile() + " has " + numSignals + " in it, but previous files have " + signalNames.length); closeInput(); stopProgressDialog(); return; } } int [] signalTypes = new int[numSignals]; for(int k=0; k<=numSignals; k++) { line = new StringBuffer(); for(int j=0; j<8; j++) { int l = getByteFromFile(); line.append((char)l); if (!isTRACDCBinary && (l == '\n' || l == '\r')) j--; } if (k == 0) continue; int l = k - nodcnt; if (k < nodcnt) l = k + numnoi - 1; String lineStr = line.toString().trim(); signalTypes[l] = TextUtils.atoi(lineStr, 0, 10); } boolean paMissingWarned = false; for(int k=0; k<=numSignals; k++) { line = new StringBuffer(); for(;;) { int l = getByteFromFile(); if (l == '\n' || l == '\r') continue; if (l == ' ') { if (line.length() != 0) break; // if name starts with blank, skip until non-blank for(;;) { l = getByteFromFile(); if (l != ' ') break; } } line.append((char)l); if (version == 9007 && line.length() >= 16) break; } int j = line.length(); int l = (j+16) / 16 * 16 - 1; if (version == 9007) { l = (j+15) / 16 * 16 - 1; } for(; j<l; j++) { int i = getByteFromFile(); if (!isTRACDCBinary && (i == '\n' || i == '\r')) { j--; continue; } } if (k == 0) continue; // convert name if there is a colon in it int startPos = 0; int openPos = line.indexOf("("); if (openPos >= 0) startPos = openPos+1; for(j=startPos; j<line.length(); j++) { if (line.charAt(j) == ':') break; if (!TextUtils.isDigit(line.charAt(j))) break; } if (j < line.length() && line.charAt(j) == ':') { l = TextUtils.atoi(line.toString().substring(startPos), 0, 10); PALine foundPALine = null; if (paList == null) { if (!paMissingWarned) System.out.println("Warning: there should be a ." + paExtension + " file with extra signal names"); paMissingWarned = true; } else { for(PALine paLine : paList) { if (paLine.number == l) { foundPALine = paLine; break; } } } if (foundPALine != null) { StringBuffer newSB = new StringBuffer(); newSB.append(line.substring(0, startPos)); newSB.append(foundPALine.string); newSB.append(line.substring(j+1)); line = newSB; } } else { if (line.indexOf(".") >= 0) { String fixedLine = removeLeadingX(line.toString()); line = new StringBuffer(); line.append(fixedLine); } } // move parenthesis from the start to the last name openPos = line.indexOf("("); if (openPos >= 0) { String parenPrefix = line.substring(0, openPos+1); int lastDot = line.lastIndexOf("."); if (lastDot >= 0) { StringBuffer newSB = new StringBuffer(); if (parenPrefix.equalsIgnoreCase("v(") && line.charAt(line.length()-1) == ')') { // just ignore the V() newSB.append(line.substring(openPos+1, lastDot+1)); newSB.append(line.substring(lastDot+1, line.length()-1)); } else { // move the parenthetical wrapper to the last dotted piece newSB.append(line.substring(openPos+1, lastDot+1)); newSB.append(parenPrefix); newSB.append(line.substring(lastDot+1)); } line = newSB; } else if (parenPrefix.equalsIgnoreCase("v(") && line.charAt(line.length()-1) == ')') { StringBuffer newSB = new StringBuffer(); // just ignore the V() newSB.append(line.substring(openPos+1, line.length()-1)); line = newSB; } } if (k < nodcnt) l = k + numnoi - 1; else l = k - nodcnt; String sigName = line.toString(); if (fileNum == 0) { signalNames[l] = sigName; } else { if (!sigName.equals(signalNames[l])) { System.out.println("Error: file " + fileURL.getFile() + " signal " + (l+1) + " is '" + sigName + "', but previous files call that signal '" + signalNames[l] + "'"); closeInput(); stopProgressDialog(); return; } } } // read (and ignore) condition information for(int c=0; c<cndcnt; c++) { int j = 0; line = new StringBuffer(); for(;;) { int l = getByteFromFile(); if (l == '\n' || l == '\r') continue; if (l == ' ') break; line.append((char)l); j++; if (j >= 16) break; } int l = (j+15) / 16 * 16 - 1; for(; j<l; j++) { int i = getByteFromFile(); if (!isTRACDCBinary && (i == '\n' || i == '\r')) { j--; continue; } } if (DEBUGCONDITIONS) System.out.println("CONDITION "+(c+1)+" IS "+line.toString()); } // read the end-of-header marker line = new StringBuffer(); if (!isTRACDCBinary) { // finish line, ensure the end-of-header for(int j=0; ; j++) { int l = getByteFromFile(); if (l == '\n' || l == '\r') break; if (j < 4) line.append((char)l); } } else { // gather end-of-header string for(int j=0; j<4; j++) line.append((char)getByteFromFile()); } if (!line.toString().equals("$&%#")) { System.out.println("HSpice header improperly terminated (got "+line.toString()+")"); closeInput(); stopProgressDialog(); return; } resetBinaryTRACDCReader(); // preprocess signal names to remove constant prefix (this code also occurs in VerilogOut.readVerilogFile) String constantPrefix = null; boolean hasPrefix = true; for(int k=0; k<numSignals; k++) { String name = signalNames[k]; int dotPos = name.indexOf('.'); if (dotPos < 0) continue; String prefix = name.substring(0, dotPos); if (constantPrefix == null) constantPrefix = prefix; if (!constantPrefix.equals(prefix)) { hasPrefix = false; break; } } if (!hasPrefix) constantPrefix = null; else { String fileName = fileURL.getFile(); int pos = fileName.lastIndexOf(File.separatorChar); if (pos >= 0) fileName = fileName.substring(pos+1); pos = fileName.lastIndexOf('/'); if (pos >= 0) fileName = fileName.substring(pos+1); pos = fileName.indexOf('.'); if (pos >= 0) fileName = fileName.substring(0, pos); if (fileName.equals(constantPrefix)) constantPrefix += "."; else constantPrefix = null; } boolean isComplex = analysisTitle.equals("AC SIGNALS"); int sweepUpper = (sweepcnt > 0) ? sweepcnt : 1; if (numFiles > 1) sweepUpper = numFiles; if (fileNum == 0) { allSignals = new Signal[numSignals][sweepUpper]; sweepNames = new String[sweepUpper]; } // setup the simulation information int sweepCounter = sweepcnt; int sweepIndex = 0; for(;;) { // get sweep info String sweepName = ""; if (sweepcnt > 0) { float sweepValue = getHSpiceFloat(false); if (eofReached) { System.out.println("EOF before sweep data"); break; } sweepName = TextUtils.formatDouble(sweepValue); if (DEBUGCONDITIONS) System.out.println("READING SWEEP NUMBER: "+sweepValue); // if there are more than 2 conditions, read extra sweep values for(int i=2; i<cndcnt; i++) { float anotherSweepValue = getHSpiceFloat(false); if (eofReached) { System.out.println("EOF reading sweep header"); break; } sweepName += "," + TextUtils.formatDouble(anotherSweepValue); if (DEBUGCONDITIONS) System.out.println(" EXTRA SWEEP NUMBER: "+anotherSweepValue); } sweepNames[fileNum + sweepIndex] = sweepName; } else if (numFiles > 1) sweepNames[fileNum] = (fileNum+1) + ""; for(int k=0; k<numSignals; k++) { String name = signalNames[k]; if (constantPrefix != null && name.startsWith(constantPrefix)) name = name.substring(constantPrefix.length()); String context = null; int lastDotPos = name.lastIndexOf('.'); if (lastDotPos >= 0) { context = name.substring(0, lastDotPos); name = name.substring(lastDotPos+1); } if (sweepcnt > 0) name += "[" + sweepName + "]"; SignalCollection scToUse = sc; if (sweepcnt > 0) scToUse = null; allSignals[k][fileNum + sweepIndex] = isComplex ? ComplexSample.createComplexSignal(scToUse, sd, name, context) : ScalarSample.createSignal(scToUse, sd, name, context); } for(;;) { // get the first number, see if it terminates float time = getHSpiceFloat(true); if (eofReached) break; // get a row of numbers for(int k=0; k<numSignals; k++) { if (isComplex) { MutableSignal<ComplexSample> signal = (MutableSignal<ComplexSample>)allSignals[(k + numnoi) % numSignals][fileNum + sweepIndex]; float realPart = getHSpiceFloat(false); float imagPart = getHSpiceFloat(false); if (signal.getSample(time) == null) signal.addSample(time, new ComplexSample(realPart, imagPart)); } else { MutableSignal<ScalarSample> signal = (MutableSignal<ScalarSample>)allSignals[(k + numnoi) % numSignals][fileNum + sweepIndex]; double val = getHSpiceFloat(false); if (signal.getSample(time) == null) signal.addSample(time, new ScalarSample(val)); } if (eofReached) { System.out.println("EOF in the middle of the data (at " + k + " out of " + numSignals +")"); break; } } if (eofReached) { System.out.println("EOF before the end of the data"); break; } } sweepCounter--; if (sweepCounter <= 0) break; sweepIndex++; eofReached = false; } if (sweepcnt > 0 || (numFiles > 1 && fileNum == numFiles-1)) { for(int k=0; k<numSignals; k++) { String name = signalNames[k]; if (constantPrefix != null && name.startsWith(constantPrefix)) name = name.substring(constantPrefix.length()); String context = null; int lastDotPos = name.lastIndexOf('.'); if (lastDotPos >= 0) { context = name.substring(0, lastDotPos); name = name.substring(lastDotPos+1); } int total = 0; for(int i=0; i<allSignals[k].length; i++) if (allSignals[k][i] != null) total++; Signal<?>[] signalCopy = new Signal[total]; int j = 0; for(int i=0; i<allSignals[k].length; i++) { if (allSignals[k][i] == null) continue; signalCopy[j] = allSignals[k][i]; j++; } if (isComplex) { SweptSample.createSignal(sc, sd, name, context, false, (Signal<ComplexSample>[])signalCopy); } else { SweptSample.createSignal(sc, sd, name, context, false, (Signal<ScalarSample>[])signalCopy); } } sc.setSweepNames(sweepNames); } closeInput(); stopProgressDialog(); if (fileNum == numFiles-1) System.out.println("Done reading " + analysisTitle + " analysis"); } /** * Method to reset the binary block pointer (done between the header and * the data). */ private void resetBinaryTRACDCReader() { binaryTRACDCSize = 0; binaryTRACDCPosition = 0; } /** * Method to read the next block of tr, sw, or ac data. * @param firstbyteread true to skip the first byte. * @return true on EOF. */ private boolean readBinaryTRACDCBlock(boolean firstbyteread) throws IOException { // read the first word of a binary block if (!firstbyteread) { if (dataInputStream.read() == -1) return true; updateProgressDialog(1); } for(int i=0; i<3; i++) if (dataInputStream.read() == -1) return true; updateProgressDialog(3); // read the number of 8-byte blocks int blocks = 0; for(int i=0; i<4; i++) { int uval = dataInputStream.read(); if (uval == -1) return true; if (isTRACDCBinarySwapped) blocks = ((blocks >> 8) & 0xFFFFFF) | ((uval&0xFF) << 24); else blocks = (blocks << 8) | uval; } updateProgressDialog(4); // skip the dummy word for(int i=0; i<4; i++) if (dataInputStream.read() == -1) return true; updateProgressDialog(4); // read the number of bytes int bytes = 0; for(int i=0; i<4; i++) { int uval = dataInputStream.read(); if (uval == -1) return true; if (isTRACDCBinarySwapped) bytes = ((bytes >> 8) & 0xFFFFFF) | ((uval&0xFF) << 24); else bytes = (bytes << 8) | uval; } updateProgressDialog(4); // now read the data if (bytes > 8192) { System.out.println("ERROR: block is " + bytes + " long, but limit is 8192"); bytes = 8192; } int amtread = dataInputStream.read(binaryTRACDCBuffer, 0, bytes); if (amtread != bytes) { System.out.println("Expected to read " + bytes + " bytes but got only " + amtread); return true; } updateProgressDialog(bytes); // read the trailer count int trailer = 0; for(int i=0; i<4; i++) { int uval = dataInputStream.read(); if (uval == -1) return true; if (isTRACDCBinarySwapped) trailer = ((trailer >> 8) & 0xFFFFFF) | ((uval&0xFF) << 24); else trailer = (trailer << 8) | uval; } if (trailer != bytes) { System.out.println("Block trailer claims block had " + trailer + " bytes but block really had " + bytes); return true; } updateProgressDialog(4); // set pointers for the buffer binaryTRACDCPosition = 0; binaryTRACDCSize = bytes; return false; } /** * Method to get the next character from the simulator. * @return the next character (EOF at end of file). */ private int getByteFromFile() throws IOException { if (byteCount == 0) { // start of HSpice file: see if it is binary or ascii int i = dataInputStream.read(); if (i == -1) return(i); updateProgressDialog(1); if (i == 0 || i == 4) { isTRACDCBinary = true; isTRACDCBinarySwapped = false; if (i == 4) isTRACDCBinarySwapped = true; binaryTRACDCBuffer = new byte[8192]; if (readBinaryTRACDCBlock(true)) return(-1); } else { isTRACDCBinary = false; return(i); } } if (isTRACDCBinary) { if (binaryTRACDCPosition >= binaryTRACDCSize) { if (readBinaryTRACDCBlock(false)) return(-1); } int val = binaryTRACDCBuffer[binaryTRACDCPosition]; binaryTRACDCPosition++; return val&0xFF; } int i = dataInputStream.read(); updateProgressDialog(1); return i; } /** * Method to get the next 4-byte integer from the simulator. * @return the next integer. */ private int getHSpiceInt() throws IOException { StringBuffer line = new StringBuffer(); for(int j=0; j<4; j++) line.append((char)getByteFromFile()); return TextUtils.atoi(line.toString().trim(), 0, 10); } /** * Method to read the next floating point number from the HSpice file. * @return the next number. Sets the global "eofReached" true on EOF. */ private float getHSpiceFloat(boolean testEOFValue) throws IOException { if (!isTRACDCBinary) { StringBuffer line = new StringBuffer(); for(int j=0; j<11; j++) { int l = getByteFromFile(); if (l == -1) { eofReached = true; return 0; } line.append((char)l); if (l == '\n' || l == '\r') j--; } String result = line.toString(); if (testEOFValue && result.equals("0.10000E+31")) { eofReached = true; return 0; } return (float)TextUtils.atof(result); } // binary format int fi0 = getByteFromFile(); int fi1 = getByteFromFile(); int fi2 = getByteFromFile(); int fi3 = getByteFromFile(); if (fi0 < 0 || fi1 < 0 || fi2 < 0 || fi3 < 0) { eofReached = true; return 0; } fi0 &= 0xFF; fi1 &= 0xFF; fi2 &= 0xFF; fi3 &= 0xFF; int fi = 0; if (isTRACDCBinarySwapped) { fi = (fi3 << 24) | (fi2 << 16) | (fi1 << 8) | fi0; } else { fi = (fi0 << 24) | (fi1 << 16) | (fi2 << 8) | fi3; } float f = Float.intBitsToFloat(fi); // the termination value (in hex) is 71 49 F2 CA if (testEOFValue && f > 1.00000000E30 && f < 1.00000002E30) { eofReached = true; return 0; } return f; } /** * Method to remove the leading "x" character in each dotted part of a string. * HSpice decides to add "x" in front of every cell name, so the path "me.you" * appears as "xme.xyou". * @param name the string from HSpice. * @return the string without leading "X"s. */ static String removeLeadingX(String name) { // remove all of the "x" characters at the start of every instance name int dotPos = -1; while (name.indexOf('.', dotPos+1) >= 0) { int xPos = dotPos + 1; if (name.length() > xPos && name.charAt(xPos) == 'x') { name = name.substring(0, xPos) + name.substring(xPos+1); } dotPos = name.indexOf('.', xPos); if (dotPos < 0) break; } return name; } }