/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: NanosimModel.java * Written by Jonathan Gainsley, Sun Microsystems. * * 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.simulation.test; import java.util.*; import java.util.regex.Pattern; import java.util.regex.Matcher; import java.io.*; public class NanosimModel extends SimulationModel { protected static final boolean DEBUG = false; /** list of jtag testers */ protected final List jtagTesters; /** list of logic settables */ protected final List logicSettables; protected double simTime; // current simulation time in ns protected double vdd; protected double timeStep; // simulation time step in ns protected final HashMap nodesToSet = new HashMap(); // key: node name, value: Double voltage protected boolean assuraRCXNetlist = false; protected boolean starRCXTNetlist = false; /** * Create a new NanosimModel to simulate the behavior of the real chip */ public NanosimModel() { super("Nanosim", "quit", "ERROR", ""); jtagTesters = new ArrayList(); logicSettables = new ArrayList(); simTime = 0; vdd = 0; } public JtagTester createJtagTester(String tckName, String tmsName, String trstbName, String tdiName, String tdobName) { if (isProcessRunning()) { System.out.println("Error: JtagTester test device must be created before process is started."); return null; } NanosimJtagTester tester = new NanosimJtagTester(this, tckName, tmsName, trstbName, tdiName, tdobName); jtagTesters.add(tester); return tester; } /** * Create a subchain tester based on the 8- or 9-wire jtag interface. * jtag[8:0] = {scan_data_return, phi2_return, phi1_return, rd, wr, phi1, phi2, sin, mc*} * Note that mc is not present on older designs, so they are jtag[8:1]. * @param jtagInBus the name of the 9-bit wide input bus, i.e. "jtagIn" or "jtagIn[8:0]" * @param jtagOutBus the name of the 9-bit wide output bus, i.e. "jtagOut" or "jtagOut[8:0]" */ public JtagTester createJtagSubchainTester(String jtagInBus, String jtagOutBus) { if (isProcessRunning()) { System.out.println("Error: JtagTester test device must be created before process is started."); return null; } NanosimJtagSubchainTester tester = new NanosimJtagSubchainTester(this, jtagInBus, jtagOutBus); jtagTesters.add(tester); return tester; } /** * Create a subchain tester based on the 5-wire jtag interface. * @param phi2 name of the phi2 signal * @param phi1 name of the phi1 signal * @param write name of the write signal * @param read name of the read signal * @param sin name of the scan data in signal * @param sout name of the scan data out signal */ public JtagTester createJtagSubchainTester(String phi2, String phi1, String write, String read, String sin, String sout) { if (isProcessRunning()) { System.out.println("Error: JtagTester test device must be created before process is started."); return null; } NanosimJtagSubchainTester tester = new NanosimJtagSubchainTester(this, phi2, phi1, write, read, sin, sout); jtagTesters.add(tester); return tester; } public LogicSettable createLogicSettable(String portName) { if (isProcessRunning()) { System.out.println("Error: LogicSettable test device must be created before process is started."); return null; } NanosimLogicSettable ls = new NanosimLogicSettable(this, portName); logicSettables.add(ls); return ls; } public LogicSettable createLogicSettable(List portNames) { if (portNames == null || portNames.size() < 1) { System.out.println("Error: createLogicSettable given null or empty list of ports"); return null; } System.out.println("Error: createLogicSettable(List) is not supported by Nanosim, only using first port "+portNames.get(0)); return new NanosimLogicSettable(this, (String)portNames.get(0)); } public void disableNode(String node) { issueCommand("force_node_v v=0 no="+node); waitNS(timeStep); } public void enableNode(String node) { issueCommand("rel_node_v no="+node); waitNS(timeStep); } public double getSimulationTime() { return simTime; } boolean start_(String command, String simFile, int recordSim) { // first, find out the nanosim version, as that determines the prompt in interactive mode // setup the reader which will read the output of the process PipedOutputStream ostream = new PipedOutputStream(); BufferedReader reader; try { PipedInputStream istream = new PipedInputStream(ostream); reader = new BufferedReader(new InputStreamReader(istream)); } catch (java.io.IOException e) { System.out.println("Unable to create pipe to process output: "+e.getMessage()); return false; } ExecProcess process = new ExecProcess(command+" --version", null, null, ostream, ostream); process.start(); boolean found = false; String version = null; StringBuffer buf = new StringBuffer(); try { String line; while ((line = reader.readLine()) != null) { buf.append(line+"\n"); if (found) continue; String [] args = line.split("\\s+"); if (args.length < 4) continue; if (args[1].equals("Version")) { version = args[3]; setPrompt("Ver "+version+" >"); found = true; } } reader.close(); } catch (java.io.IOException e) { if (!found) { System.out.println("Error determining nanosim version: "+e.getMessage()); System.out.println(buf); return false; } } if (!found) { System.out.println("Error determining nanosim version"); System.out.println(buf); return false; } if (DEBUG) System.out.println("Using nanosim version "+version); // check if simfile is from assura RCX // if it is, we will have to replace hierarchy delimiter '.' with '/' try { BufferedReader freader = new BufferedReader(new FileReader(simFile)); // PROGRAM will be in the first 10 or so lines, so search up till line 20 for (int i=0; i<20; i++) { String line = freader.readLine(); if (line == null) break; if (line.matches("\\* PROGRAM .*?assura.*")) { assuraRCXNetlist = true; System.out.println("Info: Running on Assura extracted netlist, will replace all '.' in net names with '/'"); break; } else if (line.matches("\\*|PROGRAM .*?Star-RCXT.*")) { starRCXTNetlist = true; System.out.println("Info: Running on Star-RCXT extracted netlist, will replace all '.x' in net names with '/'"); break; } } } catch (IOException e) { System.out.println(e.getMessage()); return false; } // max sim time (-t) is 90071s String cmd = command+" -n "+simFile+" -i -t 90071s -o "+simFile; if (!startProcess(cmd, null, null, simFile+".run")) return false; // get value of vdd vdd = getNodeVoltage("vdd"); if (DEBUG) System.out.println("Using VDD of "+vdd); timeStep = getSimTres(); if (DEBUG) System.out.println("Using time step of "+timeStep+" ns"); // initialize test devices for (Iterator it = logicSettables.iterator(); it.hasNext(); ) { NanosimLogicSettable ls = (NanosimLogicSettable)it.next(); if (!ls.init()) { System.out.println("LogicSettable initialization failed, aborting."); return false; } } for (Iterator it = jtagTesters.iterator(); it.hasNext(); ) { JtagTester tester = (JtagTester)it.next(); tester.reset(); } return true; } /** * Get the voltage of VDD for the simulation. The simulation must have been * started for this method to return a valid value. * @return the VDD voltage, in volts. */ public double getVdd() { return vdd; } protected static final Pattern patSimTime = Pattern.compile("The simulation time is\\s+: ([0-9\\.]+) ns"); public void wait(float seconds) { waitNS(seconds*1.0e9); } /** * Wait the specified number of nano-seconds. In this case, this means * run the simulator until that point in time, when control will return * to the test software. * @param nanoseconds the time to wait (simulate) in nanoseconds */ public void waitNS(double nanoseconds) { waitNS(nanoseconds, true); } // internal only method; applyVoltages calls this with the boolean false so no infinite recursion protected void waitNS(double nanoseconds, boolean applyVoltages) { /* issueCommand("get_sim_time"); StringBuffer buf = getLastCommandOutput(); Matcher m = patSimTime.matcher(buf.toString()); double cur_time = 0; if (m.find()) { try { cur_time = Double.parseDouble(m.group(1)); } catch (NumberFormatException e) { System.out.println("Error in get_sim_time, could not parse time "+m.group(1)+" from:\n"+buf); return; } } else { System.out.println("Error determining current time, get_sim_time returned: "+buf); return; } */ // first apply any voltages that have been set if (applyVoltages) applyVoltages(); if (nanoseconds < timeStep) { System.out.println("Warning: cannot run simulator in increments less than time step (currently "+timeStep+" ns), setting it to "+timeStep+" ns"); nanoseconds = timeStep; } // round wait time to the nearest time step, otherwise nanosim gets confused nanoseconds = (int)(nanoseconds/timeStep) * timeStep; // calculate stop time (time at which to set breakpoint) double stopTime = simTime + nanoseconds; // set break point issueCommand("set_time_break "+stopTime+"ns"); issueCommand("cont_sim"); simTime = stopTime; } public void waitPS(double ps) { waitNS(ps/1000.0); } public double getTimeNS() { return simTime; /* issueCommand("get_sim_time"); StringBuffer buf = getLastCommandOutput(); Matcher m = patSimTime.matcher(buf.toString()); double cur_time = 0; if (m.find()) { try { cur_time = Double.parseDouble(m.group(1)); } catch (NumberFormatException e) { System.out.println("Error in get_sim_time, could not parse time "+m.group(1)+" from:\n"+buf); return cur_time; } } else { System.out.println("Error determining current time, get_sim_time returned: "+buf); return cur_time; } return cur_time; */ } protected static final Pattern getSimTime_tres = Pattern.compile("The engine time resolution is\\s+: ([0-9\\.]+) ns"); /** * Get the simulation's time step * @return the simulation time step in nanoseconds */ protected double getSimTres() { issueCommand("get_sim_time"); StringBuffer buf = getLastCommandOutput(); Matcher m = getSimTime_tres.matcher(buf); if (m.find()) { try { double d = Double.parseDouble(m.group(1)); return d; } catch (NumberFormatException e) { System.out.println("Error converting string to double in "+m.group(0)+": "+e.getMessage()); } } double d = 0.01; // default is 10ps System.out.println("Cannot determine time step, using default of "+d+" ns"); return d; } // ==================================================================== // ==================================================================== // Get/Set voltages // ==================================================================== // ==================================================================== /** * Force node to a state. Note that 1 is high and 0 is low. * The node is * case-insensitive, and may be a hierarchical spice name, such as 'Xtop.Xfoo.net@12'. * It should match the name from the spice file that nanosim is simulating. * @param node the hierarchical spice node name * @param state the state to set to, must be 1 or 0. */ public void setNodeState(String node, int state) { node = node.toLowerCase(); if (state != 0 && state != 1) { System.out.println("Illegal state passed to setNodeState: "+state+". Expected 0 or 1."); return; } setNodeVoltage(node, state*vdd); } /** * Set a node in the nanosim simulation to the specified voltage. * The node is * case-insensitive, and may be a hierarchical spice name, such as 'Xtop.Xfoo.net@12'. * It should match the name from the spice file that nanosim is simulating. * @param node the hierarchical spice node name * @param voltage the voltage to set the node to */ public void setNodeVoltage(String node, double voltage) { node = node.toLowerCase(); //issueCommand("set_node_v "+node+" "+voltage+" r=5 l=1n"); nodesToSet.put(node, new Double(voltage)); } /** * This is used to apply all voltages at the same time for the current time step. */ protected void applyVoltages() { // release all forced nodes releaseNodes(new ArrayList(nodesToSet.keySet())); // all nodes are released, now apply all the values for (Iterator it = nodesToSet.entrySet().iterator(); it.hasNext(); ) { Map.Entry entry = (Map.Entry)it.next(); String node = (String)entry.getKey(); Double voltage = (Double)entry.getValue(); if (assuraRCXNetlist) { node = node.replaceAll("\\.", "/"); } else if (starRCXTNetlist) { if (node.startsWith("x")) node = node.substring(1); node = node.replaceAll("\\.x?", "/"); } issueCommand("force_node_v v="+voltage.doubleValue()+" no="+node); } nodesToSet.clear(); waitNS(timeStep, false); } // release the list of nodes (Strings) public void releaseNodes(List nodes) { // NOTE JKG 7/21/05: // Ajanta and I have found that release does not always work, // and sometimes the node remains "forced". Issuing another // release seems to remove the forced state, though, so the // following code issues releases until it is released // (or stops after 10 tries) int i=0; int nodesReleased = 0; while (nodesReleased < nodes.size() && i<10) { // make sure all nodes to set are not forced nodesReleased = 0; boolean releasedIssued = false; for (Iterator it = nodes.iterator(); it.hasNext(); ) { String node = (String)it.next(); if (assuraRCXNetlist) { node = node.replaceAll("\\.", "/"); } else if (starRCXTNetlist) { if (node.startsWith("x")) node = node.substring(1); node = node.replaceAll("\\.x?", "/"); } issueCommand("get_node_info detail=on "+node); StringBuffer output = getLastCommandOutput(); if (output.indexOf("IS_FORCED: 1") != -1) { // we need to release node issueCommand("rel_node_v no="+node); releasedIssued = true; } else { nodesReleased++; // this node is already released } } if (releasedIssued) { // advance time to try apply release waitNS(timeStep, false); } i++; } } List getNodeVoltages(List nodes) { return getNodeInfo(nodes, false); } List getNodeStates(List nodes) { return getNodeInfo(nodes, true); } /** * Get the state of a node. Returns 1 or 0. * The node is * case-insensitive, and may be a hierarchical spice name, such as 'Xtop.Xfoo.net@12'. * It should match the name from the spice file that nanosim is simulating. * May return -1 if not a valid state. * @param node the hierarchical spice node name */ public int getNodeState(String node) { List list = new ArrayList(); list.add(node); List vals = getNodeInfo(list, true); if (vals != null && vals.size() > 0) return ((Number)vals.get(0)).intValue(); return -1; } /** * Get the voltage on a node. * The node is * case-insensitive, and may be a hierarchical spice name, such as 'Xtop.Xfoo.net@12'. * It should match the name from the spice file that nanosim is simulating. * @param node the hierarchical spice node name * @return the voltage, or -1 if error */ public double getNodeVoltage(String node) { List list = new ArrayList(); list.add(node); List vals = getNodeInfo(list, false); if (vals != null && vals.size() > 0) return ((Double)vals.get(0)).doubleValue(); return -1; } protected static final Pattern patNodeInfo = Pattern.compile("Node status of (.*?)\\((\\d+)\\): (.*?) \\(([0-9\\.\\-]+) V\\)"); // digital only protected static final Pattern patNodeInfo2 = Pattern.compile("Node status of (.*?)\\((\\d+)\\): (\\d+)"); /** * Gets the voltages on the nodes, unless returnState is true, in * which case it returns a list of 1's and 0's representing the logic state, * rather than the actual voltages. Also returns -2 for undefined logic state, or -1 on error. * @param nodes the nodes to query * @param returnState true to convert voltages to 1 or 0 logic states * @return a list of voltages/states, or null on error */ protected List getNodeInfo(List nodes, boolean returnState) { // apply any outstanding voltages if (nodesToSet.size() > 0) applyVoltages(); if (assuraRCXNetlist || starRCXTNetlist) { List nodesfixed = new ArrayList(); for (Iterator it = nodes.iterator(); it.hasNext(); ) { String node = (String)it.next(); if (assuraRCXNetlist) { node = node.replaceAll("\\.", "/"); } else if (starRCXTNetlist) { if (node.startsWith("x")) node = node.substring(1); node = node.replaceAll("\\.x?", "/"); } nodesfixed.add(node); } nodes = nodesfixed; } List nodeslc = new ArrayList(); StringBuffer cmd = new StringBuffer(); cmd.append("get_node_info "); for (Iterator it = nodes.iterator(); it.hasNext(); ) { String node = (String)it.next(); node = node.toLowerCase(); nodeslc.add(node); cmd.append(node+" "); } nodes = nodeslc; issueCommand(cmd.toString()); StringBuffer result = getLastCommandOutput(); String [] results = result.toString().trim().split("\n"); List voltages = new ArrayList(); List states = new ArrayList(); int nodeIndex = 0; for (int i=0; i<results.length; i++) { if (!results[i].startsWith("Node status")) continue; // quick check Matcher m = patNodeInfo.matcher(results[i]); Matcher m2 = patNodeInfo2.matcher(results[i]); String node = (String)nodes.get(nodeIndex); if (m.find()) { // don't do this check anymore, as nanosim can map the hierarchical name to the real name //if (!m.group(1).equals(node)) { // System.out.println("Error on get_info_node: expected info for node "+node+" but got info for node "+m.group(1)); // return null; //} Integer state; // state can be one of 1, 0, or U if (m.group(3).equals("1")) state = new Integer(1); else if (m.group(3).equals("0")) state = new Integer(0); else if (m.group(3).equals("U")) state = new Integer(-2); else { System.out.println("Uknown state of "+node+": "+m.group(3)+", setting it to -1 (Undefined)"); state = new Integer(-1); } Double voltage; try { voltage = new Double(m.group(4)); } catch (NumberFormatException e) { System.out.println("Error on get_info_node: NumberFormatException converting node "+node+" state/voltage ("+m.group(3)+"/"+m.group(4)+") to integer/double"); return null; } voltages.add(voltage); states.add(state); nodeIndex++; if (DEBUG) System.out.println("get_info_node "+node+": \tstate="+state+"\tvoltage="+voltage); } else if (m2.find() && returnState) { // digial only if (!m2.group(1).equals(node)) { System.out.println("Error on get_info_node: expected info for node "+node+" but got info for node "+m.group(1)); return null; } Integer state; // state can be one of 1, 0, or U if (m2.group(3).equals("1")) state = new Integer(1); else if (m2.group(3).equals("0")) state = new Integer(0); else if (m2.group(3).equals("U")) state = new Integer(-2); else { System.out.println("Uknown state of "+node+": "+m2.group(3)+", setting it to -1 (Undefined)"); state = new Integer(-1); } states.add(state); nodeIndex++; if (DEBUG) System.out.println("get_info_node "+node+": \tstate="+state); } } if (returnState) return states; return voltages; } /** * Saves the state of all nodes at the given time to an external .ic file, * using the report_node_ic command. * @param time time in nanoseconds */ public void reportNodeIC(double time) { issueCommand("report_node_ic all "+time+"ns"); } /** * Saves the state of all nodes at the current time to an external .ic file, * using the report_node_ic command. */ public void reportNodeIC() { issueCommand("report_node_ic all"); } /** Unit Test * This test requires the file sim.spi in your working dir * */ public static void main(String[] args) { NanosimModel nm = new NanosimModel(); // the example file sim.spi needs to be in your working dir nm.start("nanosim", "sim.spi", 0); nm.issueCommand("get_sim_time"); nm.finish(); } }