/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: EpicOutProcess.java * * Copyright (c) 2005 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.io.input; import com.sun.electric.Main; import com.sun.electric.database.Environment; import com.sun.electric.database.hierarchy.Cell; import com.sun.electric.database.text.TextUtils; import com.sun.electric.tool.Job; import com.sun.electric.tool.UserInterfaceExec; import com.sun.electric.tool.simulation.AnalogSignal; import com.sun.electric.tool.simulation.Simulation; import com.sun.electric.tool.simulation.Stimuli; import com.sun.electric.tool.user.ActivityLogger; import java.io.BufferedReader; import java.io.DataInputStream; import java.io.EOFException; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; import java.util.List; /** * Class for reading and displaying waveforms from Epic output. * These are contained in .out files. * This class invokes external JVM to read the EpicFile. */ public class EpicOutProcess extends Simulate implements Runnable { private Process readerProcess; private DataInputStream stdOut; private ArrayList<String> strings = new ArrayList<String>(); private final Environment launcherEnvironment; private final UserInterfaceExec userInterface; EpicOutProcess() { launcherEnvironment = Environment.getThreadEnvironment(); userInterface = new UserInterfaceExec(); } /** * Method to read an Spice output file. */ protected void readSimulationOutput(Stimuli sd, URL fileURL, Cell cell) throws IOException { // show progress reading .spo file startProgressDialog("EPIC output", fileURL.getFile()); // read the actual signal data from the .spo file boolean eof = false; try { readerProcess = invokeEpicReader(fileURL); stdOut = new DataInputStream(readerProcess.getInputStream()); (new Thread(this, "EpicReaderErrors")).start(); readEpicFile(sd); sd.setCell(cell); } catch (EOFException e) { eof = true; } int exitCode = 0; if (readerProcess != null) { try { exitCode = readerProcess.waitFor(); } catch (InterruptedException e) {} } if (eof || exitCode != 0) { String exitMsg = null; switch (exitCode) { case 0: exitMsg = "Ok, but EOF encountered"; break; case 1: exitMsg = "File not found"; break; case 2: exitMsg = "Out of memory"; break; default: exitMsg = "Error"; } Job.getUserInterface().showErrorMessage("EpicReaderProcess exited with code " + exitCode + " (" + exitMsg + ")", "EpicReaderProcess"); } // stop progress dialog stopProgressDialog(); // free memory strings = null; stdOut.close(); stdOut = null; readerProcess = null; } // private static String VERSION_STRING = ";! output_format 5.3"; private static class SigInfo { private final int minV, maxV; private final int start, len; private SigInfo(int minV, int maxV, int start, int len) { this.minV = minV; this.maxV = maxV; this.start = start; this.len = len; } } private void readEpicFile(Stimuli sd) throws IOException { char separator = '.'; sd.setSeparatorChar(separator); EpicAnalysis an = new EpicAnalysis(sd); int numSignals = 0; ContextBuilder contextBuilder = new ContextBuilder(); ArrayList<ContextBuilder> contextStack = new ArrayList<ContextBuilder>(); contextStack.add(contextBuilder); int contextStackDepth = 1; final boolean DEBUG = false; for (;;) { byte b = stdOut.readByte(); if (b == 'F') break; switch (b) { case 'V': case 'I': int sigNum = stdOut.readInt(); String name = readString(); if (DEBUG) printDebug(contextStackDepth, (char)b, name); contextBuilder.strings.add(name); byte type = b == 'V' ? EpicAnalysis.VOLTAGE_TYPE: EpicAnalysis.CURRENT_TYPE; contextBuilder.contexts.add(EpicAnalysis.getContext(type)); EpicAnalysis.EpicSignal s = new EpicAnalysis.EpicSignal(an, type, numSignals++, sigNum); s.setSignalName(name, null); break; case 'D': String down = readString(); if (DEBUG) printDebug(contextStackDepth, (char)b, down); contextBuilder.strings.add(down); if (contextStackDepth >= contextStack.size()) contextStack.add(new ContextBuilder()); contextBuilder = contextStack.get(contextStackDepth++); break; case 'U': if (DEBUG) printDebug(contextStackDepth, (char)b, null); EpicAnalysis.Context newContext = an.getContext(contextBuilder.strings, contextBuilder.contexts); contextBuilder.clear(); contextStackDepth--; contextBuilder = contextStack.get(contextStackDepth - 1); contextBuilder.contexts.add(newContext); break; default: assert false; } assert contextBuilder == contextStack.get(contextStackDepth - 1); } assert contextStackDepth == 1; an.setRootContext(an.getContext(contextBuilder.strings, contextBuilder.contexts)); an.setTimeResolution(stdOut.readDouble()); an.setVoltageResolution(stdOut.readDouble()); an.setCurrentResolution(stdOut.readDouble()); an.setMaxTime(stdOut.readDouble()); List<AnalogSignal> signals = an.getSignals(); assert numSignals == signals.size(); an.waveStarts = new int[numSignals]; an.waveLengths = new int[numSignals]; List<SigInfo> sigInfoByEpicIndex = new ArrayList<SigInfo>(); int start = 0; for (;;) { int sigNum = stdOut.readInt(); if (sigNum == -1) break; while (sigNum >= sigInfoByEpicIndex.size()) sigInfoByEpicIndex.add(null); int minV = stdOut.readInt(); int maxV = stdOut.readInt(); int len = stdOut.readInt(); assert sigInfoByEpicIndex.get(sigNum) == null; sigInfoByEpicIndex.set(sigNum, new SigInfo(minV, maxV, start, len)); assert len >= 0; start += len; } for (int i = 0; i < numSignals; i++) { EpicAnalysis.EpicSignal s = (EpicAnalysis.EpicSignal)signals.get(i); SigInfo si = sigInfoByEpicIndex.get(s.sigNum); s.setBounds(si.minV, si.maxV); an.waveStarts[i] = si.start; an.waveLengths[i] = si.len; } an.setWaveFile(new File(stdOut.readUTF())); } private void printDebug(int level, char cmd, String arg) { StringBuilder sb = new StringBuilder(); while (level-- > 0) sb.append(' '); sb.append(cmd); if (arg != null) { sb.append(' '); sb.append(arg); } System.out.println(sb); } private String readString() throws IOException { int stringIndex = stdOut.readInt(); if (stringIndex == -1) return null; if (stringIndex == strings.size()) { String s = stdOut.readUTF(); strings.add(s); } return strings.get(stringIndex); } private Process invokeEpicReader(URL fileURL) { // see if the required amount of memory is already present Runtime runtime = Runtime.getRuntime(); String program = "java"; String javaHome = System.getProperty("java.home"); if (javaHome != null) program = javaHome + File.separator + "bin" + File.separator + program; int maxMemWanted = Simulation.getSpiceEpicMemorySize(); // get location of jar file URL electric = Main.class.getResource("Main.class"); if (electric.getProtocol().equals("jar")) { String file = electric.getFile(); file = file.replaceAll("file:", ""); file = file.replaceAll("!.*", ""); } String command = program; command += " -cp " + Main.getJarLocation(); command += " -ss2m"; command += " -ea"; // enable assertions command += " -mx" + maxMemWanted + "m com.sun.electric.tool.io.input.EpicReaderProcess"; command += " " + fileURL; Process process = null; try { process = runtime.exec(command); System.out.println("EpicReaderProcess launched with memory limit " + maxMemWanted + "m"); } catch (java.io.IOException e) { System.out.println("EpicReaderProcess failed to launch"); } return process; } /** * This methods implements Runnable interface for thread which polls stdErr of EpicReaderProcess * and redirects it to System.out and progress indicator. */ public void run() { Environment.setThreadEnvironment(launcherEnvironment); Job.setUserInterface(userInterface); final String progressKey = "**PROGRESS "; BufferedReader stdErr = new BufferedReader(new InputStreamReader(readerProcess.getErrorStream())); try { // read from stream String line = null; while ((line = stdErr.readLine()) != null) { if (line.startsWith(progressKey)) { line = line.substring(progressKey.length()); if (line.startsWith("!")) { setProgressValue(0); setProgressNote(line.substring(1)); } else { setProgressValue(TextUtils.atoi(line)); } continue; } System.out.println("EpicReader: " + line); } stdErr.close(); } catch (java.io.IOException e) { ActivityLogger.logException(e); } } /** * Class which is used to restore Contexts from flat list of Signals. */ private static class ContextBuilder { List<String> strings = new ArrayList<String>(); List<EpicAnalysis.Context> contexts = new ArrayList<EpicAnalysis.Context>(); void clear() { strings.clear(); contexts.clear(); } } }