/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: EpicOut.java * Module to read Nanosim simulation output * * Copyright (c) 2009, 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.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.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.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; /** * Class for reading and displaying waveforms from Epic output. */ public class EpicOut extends Input<Stimuli> { /** Pattern used to split input line into pieces. */ private Pattern whiteSpace = Pattern.compile("[ \t]+"); /** Epic separator character */ private static final char separator = '.'; private List<Signal<ScalarSample>> signalsByEpicIndex; private Set<Integer> currentSignals; protected Stimuli processInput(URL fileURL, Cell cell, Stimuli sd) throws IOException { // find all ".out" files List<String> outFiles = new ArrayList<String>(); outFiles.add(fileURL.getPath()); String fileNameNoPath = TextUtils.getFileNameWithoutExtension(TextUtils.URLtoString(fileURL), true) + "_"; 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; if (!fileList[i].endsWith(".out")) continue; String newFile = topDirName + fileList[i]; if (!outFiles.contains(newFile)) outFiles.add(newFile); } Collections.sort(outFiles, TextUtils.STRING_NUMBER_ORDER); if (outFiles.size() > 1) System.out.println("Reading " + outFiles.size() + " files..."); // setup the delimiter for this stimuli sd.setNetDelimiter(" "); // open the file boolean first = true; for(String fileName : outFiles) { URL fURL = TextUtils.makeURLToFile(fileName); if (openTextInput(fURL)) return sd; // show progress reading file System.out.println("Reading Epic output file: " + fileName); startProgressDialog("Epic output", fileName); // read the actual signal data from the file readEpicFile(fileName, cell, sd, first); first = false; // stop progress dialog, close the file stopProgressDialog(); closeInput(); } return sd; } private void readEpicFile(String fileName, Cell cell, Stimuli sd, boolean first) throws IOException { double curTime = 0; SignalCollection sc; if (first) { signalsByEpicIndex = new ArrayList<Signal<ScalarSample>>(); currentSignals = new HashSet<Integer>(); sc = Stimuli.newSignalCollection(sd, "TRANS SIGNALS"); } else { sc = sd.findSignalCollection("TRANS SIGNALS"); } double timeResolution = 1; double voltageResolution = 1; double currentResolution = 1; for (;;) { String line = getLine(); if (line == null) break; updateProgressDialog(line.length()+1); // ignore blank lines and comments if (line.length() == 0) continue; char ch = line.charAt(0); if (ch == ';' || Character.isSpaceChar(ch)) continue; // handle commands if (ch == '.') { String[] split = whiteSpace.split(line); if (split[0].equals(".index") && split.length == 4) { // get the signal name String name = split[1]; if (name.startsWith("v(") && name.endsWith(")")) { name = name.substring(2, name.length() - 1); } else if (name.startsWith("i(") && name.endsWith(")")) { name = name.substring(2, name.length() - 1); } else if (name.startsWith("i1(") && name.endsWith(")")) { name = name.substring(3, name.length() - 1); } String context = null; String wholeName = name; int sepPos = name.lastIndexOf(separator); if (sepPos >= 0) { context = name.substring(0, sepPos); name = name.substring(sepPos+1); } // get the signal number int sigNum = TextUtils.atoi(split[2]); // see if it is current or voltage boolean isCurrent; if (split[3].equals("v")) isCurrent = false; else if (split[3].equals("i")) isCurrent = true; else { System.out.println("Error in line " + lineReader.getLineNumber() + ": Unknown waveform type: " + line); break; } if (first) { // create the signal while (signalsByEpicIndex.size() <= sigNum) signalsByEpicIndex.add(null); Signal<ScalarSample> s = signalsByEpicIndex.get(sigNum); if (s == null) { s = ScalarSample.createSignal(sc, sd, name, context); signalsByEpicIndex.set(sigNum, s); if (isCurrent) currentSignals.add(Integer.valueOf(sigNum)); } } else { // check the signal Signal<ScalarSample> s = signalsByEpicIndex.get(sigNum); if (s == null) { System.out.println("WARNING: File " + fileName + " has unknown signal " + sigNum); } else { boolean nameSame = name.equals(s.getSignalName()); if (context != null) nameSame &= context.equals(s.getSignalContext()); if (!nameSame) { System.out.println("WARNING: File " + fileName + " signal " + sigNum + " is called '" + wholeName + "' but in an earlier file is called '" + s.getFullName() + "'"); } } } continue; } // handle number resolutions if (split[0].equals(".time_resolution") && split.length == 2) { timeResolution = TextUtils.atof(split[1]) * 1e-9; continue; } if (split[0].equals(".current_resolution") && split.length == 2) { currentResolution = TextUtils.atof(split[1]); continue; } if (split[0].equals(".voltage_resolution") && split.length == 2) { voltageResolution = TextUtils.atof(split[1]); continue; } // ignore known commands if (split[0].equals(".vdd") && split.length == 2) continue; if (split[0].equals(".simulation_time") && split.length == 2) continue; if (split[0].equals(".high_threshold") && split.length == 2) continue; if (split[0].equals(".low_threshold") && split.length == 2) continue; if (split[0].equals(".nnodes") && split.length == 2) continue; if (split[0].equals(".nelems") && split.length == 2) continue; if (split[0].equals(".extra_nodes") && split.length == 2) continue; if (split[0].equals(".bus_notation") && split.length == 4) continue; if (split[0].equals(".hier_separator") && split.length == 2) continue; if (split[0].equals(".case") && split.length == 2) continue; System.out.println("Error in line " + lineReader.getLineNumber() + ": Unrecognized Epic command: " + line); break; } // handle time and data if (ch >= '0' && ch <= '9') { String[] split = whiteSpace.split(line); if (split.length > 1) { int num = TextUtils.atoi(split[0]); double value = TextUtils.atof(split[1]); if (currentSignals.contains(Integer.valueOf(num))) value *= currentResolution; else value *= voltageResolution; MutableSignal<ScalarSample> s = (MutableSignal<ScalarSample>)signalsByEpicIndex.get(num); if (s == null) { System.out.println("Error in line " + lineReader.getLineNumber() + ": Signal " + num + " not defined"); break; } try { s.addSample(curTime, new ScalarSample(value)); } catch (RuntimeException e) { } } else { long num; try { num = Long.parseLong(split[0]); } catch (NumberFormatException e) { num = 0; } curTime = num * timeResolution; } continue; } System.out.println("Error in line " + lineReader.getLineNumber() + ": Unrecognized Epic line: " + line); break; } } }