/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: IRSIM.java * * Copyright (c) 2010, 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.irsim; import com.sun.electric.api.irsim.IAnalyzer; import com.sun.electric.database.hierarchy.Cell; import com.sun.electric.database.variable.VarContext; import com.sun.electric.lib.LibFile; import com.sun.electric.technology.Technology; import com.sun.electric.technology.technologies.Schematics; import com.sun.electric.tool.Job; import com.sun.electric.tool.extract.ExtractedPBucket; import com.sun.electric.tool.extract.RCPBucket; import com.sun.electric.tool.extract.TransistorPBucket; import com.sun.electric.tool.io.FileType; import com.sun.electric.tool.io.output.IRSIM.IRSIMPreferences; import com.sun.electric.tool.simulation.BusSample; import com.sun.electric.tool.simulation.DigitalSample; import com.sun.electric.tool.simulation.Engine; import com.sun.electric.tool.simulation.MutableSignal; import com.sun.electric.tool.simulation.Signal; import com.sun.electric.tool.simulation.SignalCollection; import com.sun.electric.tool.simulation.SimulationTool; import com.sun.electric.tool.simulation.Stimuli; import com.sun.electric.tool.user.User; import com.sun.electric.tool.user.waveform.Panel; import com.sun.electric.tool.user.waveform.WaveSignal; import com.sun.electric.tool.user.waveform.WaveformWindow; import com.sun.electric.util.TextUtils; import com.sun.electric.util.config.Configuration; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import javax.swing.ProgressMonitorInputStream; import javax.swing.SwingUtilities; /** * Class for interfacing to the IRSIM simulator by reflection. */ public class IRSIM implements Engine, IAnalyzer.GUI { /** initial size of simulation window: 10ns */ private static final double DEFIRSIMTIMERANGE = 10.0E-9f; private static boolean IRSIMAvailable; private static boolean IRSIMChecked = false; /** * Method to tell whether the IRSIM simulator is available. * IRSIM is packaged separately because it is from Stanford University. * This method dynamically figures out whether the IRSIM module is present by using reflection. * @return true if the IRSIM simulator is available. */ public static boolean hasIRSIM() { if (!IRSIMChecked) { IRSIMChecked = true; IRSIMAvailable = Configuration.lookup(IAnalyzer.class) != null; if (!IRSIMAvailable) { TextUtils.recordMissingComponent("IRSIM"); } } return IRSIMAvailable; } /** * Method to run the IRSIM simulator on a given cell, context or file. * Uses reflection to find the IRSIM simulator (if it exists). * @param cell the Cell to simulate. * @param context the context to the cell to simulate. * @param fileName the name of the file with the netlist. If this is null, simulate the cell. * If this is not null, ignore the cell and simulate the file. */ public static void runIRSIM(Cell cell, VarContext context, String fileName, IRSIMPreferences ip, boolean doNow) { try { URL parameterURL = null; String parameterFile = ip.parameterFile.trim(); if (parameterFile.length() > 0) { File pf = new File(parameterFile); if (pf != null && pf.exists()) { parameterURL = TextUtils.makeURLToFile(parameterFile); } else { parameterURL = LibFile.getLibFile(parameterFile); } if (parameterURL == null) { System.out.println("Cannot find parameter file: " + parameterFile); } } IAnalyzer ianalyzer = Configuration.lookup(IAnalyzer.class); final IRSIM irsim = new IRSIM(); final IAnalyzer.EngineIRSIM analyzer = ianalyzer.createEngine(irsim, ip.steppingModel, parameterURL, ip.irDebug, SimulationTool.isIRSIMShowsCommands(), SimulationTool.isIRSIMDelayedX()); irsim.a = analyzer; synchronized (analyzer) { // Load network if (cell != null) { System.out.println("Loading netlist for " + cell.noLibDescribe() + "..."); } else { System.out.println("Loading netlist for file " + fileName + "..."); } // Load network if (fileName == null) { // generate the components directly List<Object> components = com.sun.electric.tool.io.output.IRSIM.getIRSIMComponents(cell, context, ip); Technology layoutTech = Schematics.getDefaultSchematicTechnology(); double lengthOff = Schematics.getDefaultSchematicTechnology().getGateLengthSubtraction() / layoutTech.getScale(); // load the circuit from memory for (Object obj : components) { ExtractedPBucket pb = (ExtractedPBucket) obj; if (pb instanceof TransistorPBucket) { TransistorPBucket tb = (TransistorPBucket) pb; analyzer.putTransistor(tb.gateName, tb.sourceName, tb.drainName, tb.getTransistorLength(lengthOff), tb.getTransistorWidth(), tb.getActiveArea(), tb.getActivePerim(), tb.ni.getAnchorCenterX(), tb.ni.getAnchorCenterY(), tb.getType() == 'n'); } else if (pb instanceof RCPBucket) { RCPBucket rcb = (RCPBucket) pb; switch (rcb.getType()) { case 'r': analyzer.putResistor(rcb.net1, rcb.net2, rcb.rcValue); break; case 'C': analyzer.putCapacitor(rcb.net1, rcb.net2, rcb.rcValue); break; } } } } else { // get a pointer to to the file with the network (.sim file) URL fileURL = TextUtils.makeURLToFile(fileName); if (irsim.inputSim(fileURL)) { return; } } analyzer.finishNetwork(); irsim.sd.setEngine(irsim); irsim.sigCollection = Stimuli.newSignalCollection(irsim.sd, "SIGNALS"); irsim.sd.setSeparatorChar('/'); analyzer.convertStimuli(); irsim.sd.setCell(cell); // make a waveform window if (doNow) { WaveformWindow.showSimulationDataInNewWindow(irsim.sd); WaveformWindow ww = irsim.sd.getWaveformWindow(); ww.setDefaultHorizontalRange(0.0, DEFIRSIMTIMERANGE); ww.setMainXPositionCursor(DEFIRSIMTIMERANGE / 5.0 * 2.0); ww.setExtensionXPositionCursor(DEFIRSIMTIMERANGE / 5.0 * 3.0); irsim.ww = ww; analyzer.init(); } else { SwingUtilities.invokeLater(new Runnable() { public void run() { WaveformWindow.showSimulationDataInNewWindow(irsim.sd); WaveformWindow ww = irsim.sd.getWaveformWindow(); ww.setDefaultHorizontalRange(0.0, DEFIRSIMTIMERANGE); ww.setMainXPositionCursor(DEFIRSIMTIMERANGE / 5.0 * 2.0); ww.setExtensionXPositionCursor(DEFIRSIMTIMERANGE / 5.0 * 3.0); irsim.ww = ww; analyzer.init(); } }); } } } catch (Exception e) { System.out.println("Unable to run the IRSIM simulator"); e.printStackTrace(System.out); } } private IAnalyzer.EngineIRSIM a; private WaveformWindow ww; /** the SignalCollection being displayed */ private SignalCollection sigCollection; private Collection<GuiSignalImpl> guiSigCollection = new ArrayList<GuiSignalImpl>(); private final Stimuli sd = new Stimuli(); // Engine /** * Returns FileType of vectors file. */ public FileType getVectorsFileType() { return FileType.IRSIMVECTOR; } /** * Returns current Stimuli. */ public Stimuli getStimuli() { return sd; } /** * Method to reload the circuit data. */ public void refresh() { } /** * Method to update the simulation (because some stimuli have changed). */ public void update() { a.playVectors(); } /** * Method to set the currently-selected signal high at the current time. */ public void setSignalHigh() { List<Signal<?>> signals = ww.getHighlightedNetworkNames(); if (signals.isEmpty()) { Job.getUserInterface().showErrorMessage("Must select a signal before setting it High", "No Signals Selected"); return; } for (Signal<?> sig : signals) { String signalName = sig.getFullName().replace('.', '/'); a.newContolPoint(signalName, ww.getMainXPositionCursor(), IAnalyzer.LogicState.LOGIC_1); } if (SimulationTool.isBuiltInResimulateEach()) { a.playVectors(); } } /** * Method to set the currently-selected signal low at the current time. */ public void setSignalLow() { List<Signal<?>> signals = ww.getHighlightedNetworkNames(); if (signals.isEmpty()) { Job.getUserInterface().showErrorMessage("Must select a signal before setting it Low", "No Signals Selected"); return; } for (Signal<?> sig : signals) { String signalName = sig.getFullName().replace('.', '/'); a.newContolPoint(signalName, ww.getMainXPositionCursor(), IAnalyzer.LogicState.LOGIC_0); } if (SimulationTool.isBuiltInResimulateEach()) { a.playVectors(); } } /** * Method to set the currently-selected signal undefined at the current time. */ public void setSignalX() { List<Signal<?>> signals = ww.getHighlightedNetworkNames(); if (signals.isEmpty()) { Job.getUserInterface().showErrorMessage("Must select a signal before setting it Undefined", "No Signals Selected"); return; } for (Signal<?> sig : signals) { String signalName = sig.getFullName().replace('.', '/'); a.newContolPoint(signalName, ww.getMainXPositionCursor(), IAnalyzer.LogicState.LOGIC_X); } if (SimulationTool.isBuiltInResimulateEach()) { a.playVectors(); } } /** * Method to set the currently-selected signal to have a clock with a given period. */ public void setClock(double period) { System.out.println("IRSIM CANNOT HANDLE CLOCKS YET"); } /** * Method to show information about the currently-selected signal. */ public void showSignalInfo() { List<Signal<?>> signals = ww.getHighlightedNetworkNames(); if (signals.isEmpty()) { Job.getUserInterface().showErrorMessage("Must select a signal before displaying it", "No Signals Selected"); return; } for (Signal<?> sig : signals) { a.showSignalInfo(findGuiSignal(sig)); } } /** * Method to remove all stimuli from the currently-selected signal. */ public void removeStimuliFromSignal() { List<Signal<?>> signals = ww.getHighlightedNetworkNames(); if (signals.size() != 1) { Job.getUserInterface().showErrorMessage("Must select a single signal on which to clear stimuli", "No Signals Selected"); return; } Signal<?> sig = signals.get(0); sig.clearControlPoints(); a.clearControlPoints(findGuiSignal(sig)); if (SimulationTool.isBuiltInResimulateEach()) { a.playVectors(); } } /** * Method to remove the selected stimuli. * @return true if stimuli were deleted. */ public boolean removeSelectedStimuli() { boolean found = false; for (Iterator<Panel> it = ww.getPanels(); it.hasNext();) { Panel wp = it.next(); for (WaveSignal ws : wp.getSignals()) { if (!ws.isHighlighted()) { continue; } double[] selectedCPs = ws.getSelectedControlPoints(); if (selectedCPs == null) { continue; } for (int i = 0; i < selectedCPs.length; i++) { if (a.clearControlPoint(findGuiSignal(ws.getSignal()), selectedCPs[i])) { found = true; } } } } if (!found) { System.out.println("There are no selected control points to remove"); return false; } // resimulate if requested if (SimulationTool.isBuiltInResimulateEach()) { a.playVectors(); } return true; } /** * Method to remove all stimuli from the simulation. */ public void removeAllStimuli() { for (Iterator<Panel> it = ww.getPanels(); it.hasNext();) { Panel wp = it.next(); for (WaveSignal ws : wp.getSignals()) { ws.getSignal().clearControlPoints(); } } a.clearAllVectors(); if (SimulationTool.isBuiltInResimulateEach()) { a.playVectors(); } } /** * Method to save the current stimuli information to disk. * @param stimuliFile file to save stimuli information */ public void saveStimuli(File stimuliFile) throws IOException { a.saveStimuli(stimuliFile); } /** * Method to restore the current stimuli information from URL. * @param reader Reader with stimuli information */ public void restoreStimuli(URL stimuliURL) throws IOException { if (stimuliURL == null) { throw new NullPointerException(); } Reader reader = new InputStreamReader(stimuliURL.openStream()); try { // remove all vectors a.clearAllVectors(); for (Iterator<Panel> it = ww.getPanels(); it.hasNext();) { Panel wp = it.next(); for (WaveSignal ws : wp.getSignals()) { ws.getSignal().clearControlPoints(); } } a.restoreStimuli(reader); } finally { reader.close(); } } // IAnalyzer.GUI public IAnalyzer.GuiSignal makeSignal(String name) { // make a signal for it int slashPos = name.lastIndexOf('/'); MutableSignal<DigitalSample> sig = slashPos >= 0 ? DigitalSample.createSignal(sigCollection, sd, name.substring(slashPos + 1), name.substring(0, slashPos)) : DigitalSample.createSignal(sigCollection, sd, name, null); GuiSignalImpl sigImpl = new GuiSignalImpl(sig, null); guiSigCollection.add(sigImpl); return sigImpl; } public void createBus(String busName, IAnalyzer.GuiSignal ... subsigs) { Signal<DigitalSample>[] sigArray = (Signal<DigitalSample>[])new Signal[subsigs.length]; for (int i = 0; i < subsigs.length; i++) { IAnalyzer.GuiSignal guiSig = subsigs[i]; Signal<DigitalSample> sigImpl = (Signal<DigitalSample>)((GuiSignalImpl)guiSig).impl; sigArray[i] = sigImpl; } Signal<BusSample<DigitalSample>> busSignal = BusSample.createSignal(sigCollection, sd, busName, null, true, sigArray); GuiSignalImpl sigImpl = new GuiSignalImpl(busSignal, subsigs); guiSigCollection.add(sigImpl); } public void makeBusSignals(List<IAnalyzer.GuiSignal> sigList) { List<Signal<?>> sigListImpl = new ArrayList<Signal<?>>(); HashMap<Signal<?>,GuiSignalImpl> sigToGui = new HashMap<Signal<?>,GuiSignalImpl>(); for (IAnalyzer.GuiSignal sig: sigList) { Signal<DigitalSample> sigImpl = (Signal<DigitalSample>)((GuiSignalImpl)sig).impl; sigListImpl.add(sigImpl); GuiSignalImpl old = sigToGui.put(sigImpl, (GuiSignalImpl)sig); if (old != null) throw new IllegalArgumentException("Duplicate " + old); } sd.makeBusSignals(sigListImpl, sigCollection); for (Signal<?> sig: sigCollection.getSignals()) { Signal<?>[] busMembers = sig.getBusMembers(); if (busMembers == null) continue; IAnalyzer.GuiSignal[] memberArray = new IAnalyzer.GuiSignal[busMembers.length]; for (int i = 0; i < busMembers.length; i++) { GuiSignalImpl ss = sigToGui.get(busMembers[i]); if (ss == null) throw new IllegalArgumentException(); memberArray[i] = ss; } guiSigCollection.add(new GuiSignalImpl(sig, memberArray)); } } public Collection<IAnalyzer.GuiSignal> getSignals() { return Collections.<IAnalyzer.GuiSignal>unmodifiableCollection(guiSigCollection); } public void setMainXPositionCursor(double curTime) { if (SimulationTool.isBuiltInAutoAdvance()) { ww.setMainXPositionCursor(curTime + 10.0 / 1000000000.0); } } public void openPanel(Collection<IAnalyzer.GuiSignal> sigs) { for (IAnalyzer.GuiSignal guiSig : sigs) { Signal<?> sig = ((GuiSignalImpl)guiSig).impl; int height = User.getWaveformDigitalPanelHeight(); Panel wp = new Panel(ww, height); wp.makeSelectedPanel(-1, -1); new WaveSignal(wp, sig); } } public void closePanels() { ww.clearHighlighting(); List<Panel> allPanels = new ArrayList<Panel>(); for (Iterator<Panel> it = ww.getPanels(); it.hasNext();) { allPanels.add(it.next()); } for (Panel wp : allPanels) { wp.closePanel(); } } public double getMaxPanelTime() { double maxPanelTime = Double.NEGATIVE_INFINITY; Iterator<Panel> it = ww.getPanels(); if (it.hasNext()) { Panel wp = it.next(); maxPanelTime = Math.max(maxPanelTime, wp.getMaxXAxis()); } return maxPanelTime; } public void repaint() { ww.repaint(); } /** * Returns canonic char for ignore-case comparison . * This is the same as Character.toLowerCase(Character.toUpperCase(ch)). * @param ch given char. * @return canonic char for the given char. */ public char canonicChar(char ch) { return TextUtils.canonicChar(ch); } /** * Returns canonic string for ignore-case comparison . * FORALL String s1, s2: s1.equalsIgnoreCase(s2) == canonicString(s1).equals(canonicString(s2) * FORALL String s: canonicString(canonicString(s)).equals(canonicString(s)) * @param s given String * @return canonic String * Simple "toLowerCase" is not sufficient. * For example ("\u0131").equalsIgnoreCase("i") , but Character.toLowerCase('\u0131') == '\u0131' . */ public String canonicString(String s) { return TextUtils.canonicString(s); } /** * Method to parse the floating-point number in a string. * There is one reason to use this method instead of Double.parseDouble: * this method does not throw an exception if the number is invalid (or blank). * @param text the string with a number in it. * @return the numeric value. */ public double atof(String text) { return TextUtils.atof(text); } /** * Method to parse the number in a string. * <P> * There are many reasons to use this method instead of Integer.parseInt... * <UL> * <LI>This method can handle any radix. * If the number begins with "0", presume base 8. * If the number begins with "0b", presume base 2. * If the number begins with "0x", presume base 16. * Otherwise presume base 10. * <LI>This method can handle numbers that affect the sign bit. * If you give 0xFFFFFFFF to Integer.parseInt, you get a numberFormatPostFix exception. * This method properly returns -1. * <LI>This method does not require that the entire string be part of the number. * If there is extra text after the end, Integer.parseInt fails (for example "123xx"). * <LI>This method does not throw an exception if the number is invalid (or blank). * </UL> * @param s the string with a number in it. * @return the numeric value. */ public int atoi(String s) { return TextUtils.atoi(s); } /** * Method to convert a double to a string. * If the double has no precision past the decimal, none will be shown. * @param v the double value to format. * @return the string representation of the number. */ public String formatDouble(double v) { return TextUtils.formatDouble(v); } /** * Load a .sim file into memory. * * A .sim file consists of a series of lines, each of which begins with a key letter. * The key letter beginning a line determines how the remainder of the line is interpreted. * The following are the list of key letters understood. * * | units: s tech: tech format: MIT|LBL|SU * If present, this must be the first line in the .sim file. * It identifies the technology of this circuit as tech and gives a scale factor for units of linear dimension as s. * All linear dimensions appearing in the .sim file are multiplied by s to give centimicrons. * The format field signifies the sim variant. Electric only recognizes SU format. * type g s d l w x y g=gattrs s=sattrs d=dattrs * Defines a transistor of type type. Currently, type may be e or d for NMOS, or p or n for CMOS. * The name of the node to which the gate, source, and drain of the transistor are connected are given by g, s, and d respectively. * The length and width of the transistor are l and w. The next two tokens, x and y, are optional. * If present, they give the location of a point inside the gate region of the transistor. * The last three tokens are the attribute lists for the transistor gate, source, and drain. * If no attributes are present for a particular terminal, the corresponding attribute list may be absent * (i.e, there may be no g= field at all). * The attribute lists gattrs, etc. are comma-separated lists of labels. * The label names should not include any spaces, although some tools can accept label names with * spaces if they are enclosed in double quotes. In version 6.4.5 and later the default format * produced by ext2sim is SU. In this format the attribute of the gate starting with S_ is the substrate node of the fet. * The attributes of the gate, and source and substrate starting with A_, P_ are the area and perimeter * (summed for that node only once) of the source and drain respectively. This addition to the format is backwards compatible. * C n1 n2 cap * Defines a capacitor between nodes n1 and n2. The value of the capacitor is cap femtofarads. * NOTE: since many analysis tools compute transistor gate capacitance themselves from the * transistor's area and perimeter, the capacitance between a node and substrate (GND!) * normally does not include the capacitance from transistor gates connected to that node. * If the .sim file was produced by ext2sim(1), check the technology file that was used to * produce the original .ext files to see whether transistor gate capacitance is included or excluded; * see "Magic Maintainer's Manual 2 - The Technology File for details. * R node res * Defines the lumped resistance of node node to be res ohms. * r node1 node2 res * Defines an explicit resistor between nodes node1 and node2 of resistance res ohms. * N node darea dperim parea pperim marea mperim * As an alternative to computed capacitances, some tools expect the total perimeter and area * of the polysilicon, diffusion, and metal in each node to be reported in the .sim file. * The N construct associates diffusion area darea (in square centimicrons) and diffusion * perimeter dperim (in centimicrons) with node node, polysilicon area parea and perimeter pperim, * and metal area marea and perimeter mperim. This construct is technology dependent and obsolete. * = node1 node2 * Each node in a .sim file is named implicitly by having it appear in a transistor definition. * All node names appearing in a .sim file are assumed to be distinct. * Some tools, such as esim(1), recognize aliases for node names. * The = construct allows the name node2 to be defined as an alias for the name node1. * Aliases defined by means of this construct may not appear anywhere else in the .sim file. */ private boolean inputSim(URL simFileURL) { // read the file String fileName = simFileURL.getFile(); try { URLConnection urlCon = simFileURL.openConnection(); // String contentLength = urlCon.getHeaderField("content-length"); // long fileLength = -1; // try { // fileLength = Long.parseLong(contentLength); // } catch (Exception e) {} // long readSoFar = 0; InputStream inputStream = urlCon.getInputStream(); InputStreamReader is = new InputStreamReader(new ProgressMonitorInputStream(null, "import " + fileName, inputStream)); int numErrors = a.inputSim(is, fileName); inputStream.close(); System.out.println("Loaded circuit, lambda=" + a.getLambda() + "u"); return numErrors > 0; } catch (IOException e) { System.out.println("Error reading file"); return false; } } private IAnalyzer.GuiSignal findGuiSignal(Signal<?> sig) { for (GuiSignalImpl guiSignal: guiSigCollection) { if (guiSignal.impl == sig) return guiSignal; } return null; } static class GuiSignalImpl implements IAnalyzer.GuiSignal { final Signal<?> impl; private IAnalyzer.GuiSignal[] busMembers; private GuiSignalImpl(Signal<?> impl, IAnalyzer.GuiSignal[] busMembers) { this.impl = impl; this.busMembers = busMembers; } public void updateHistory(double[] time, IAnalyzer.LogicState[] value) { } public String getFullName() { return impl.getFullName(); } public String getSignalName() { return impl.getSignalName(); } public IAnalyzer.GuiSignal[] getBusMembers() { return busMembers; } public void addControlPoint(double time) { impl.addControlPoint(time); } public void removeControlPoint(double time) { impl.removeControlPoint(time); } public void addSample(double t, IAnalyzer.LogicState v) { DigitalSample.Value value; switch (v) { case LOGIC_1: value = DigitalSample.Value.HIGH; break; case LOGIC_0: value = DigitalSample.Value.LOW; break; case LOGIC_X: value = DigitalSample.Value.X; break; default: throw new AssertionError(); } MutableSignal<DigitalSample> mutable = (MutableSignal<DigitalSample>)impl; if (mutable.getSample(t)==null) mutable.addSample(t, DigitalSample.getSample(value, DigitalSample.Strength.LARGE_CAPACITANCE)); } } }