/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: VerilogModel.java * Written by Jonathan Gainsley, Sun Microsystems. * * Copyright (c) 2004 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.io.*; import java.util.List; import java.util.Iterator; import java.util.ArrayList; import java.util.HashMap; import java.net.URL; /** * Create a Verilog Model that simulates the behavior of a Device Under Test. * This model replaces the actual chip under test by providing 'test devices' * that replace actual hardware test measurement devices, such as the jtag tester. * <P> * Example: * <code><pre> * // create a new VerilogModel * VerilogModel vm = new VerilogModel(); * * // create a jtag tester which connects to the given port names on the model * JtagTester jtag = vm.createJtagTester("TCK", "TMS", "TRSTb", "TDI", "TDOb"); * * // create a logic settable device to control the given port * LogicSettable enable = vm.createLogicSettable("enable"); * * // start the verilog process * vm.start("verilog", "chipModel.v", false); * * .... * * vm.finish(); * * </pre> * </code> * Note that you need to call <i>finish()</i> to end the simulator. Also, devices can * only be created before the process is started. */ public class VerilogModel extends SimulationModel { private static final boolean DEBUG = false; // general debugging messages private static final boolean DEBUGOUTPUT = false; // for debugging the verilog stdout parser /** Do not record simulation */ public static final int NORECORD = 0; /** Use 'dumpvars' to record simulation */ public static final int DUMPVARS = 1; /** Use 'recordvars' to record simulation */ public static final int RECORDVARS = 2; /** list of jtag testers */ private final List jtagTesters; /** list of logic settables */ private final List logicSettables; /** list of aliased signal names at top level */ private final AllAliasedNames aliased; /** number of ticks since simulation started */ private int ticks; public static final String jtagControllerFile = "jtagController.v"; /** * Create a new VerilogModel to simulate the behavior of the real chip. */ public VerilogModel() { super("Verilog", "$finish;", "Error!", "C%% > "); jtagTesters = new ArrayList(); logicSettables = new ArrayList(); aliased = new AllAliasedNames(); ticks = 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 Verilog process is started."); return null; } VerilogJtagTester tester = new VerilogJtagTester(this, tckName, tmsName, trstbName, tdiName, tdobName); jtagTesters.add(tester); return tester; } public JtagTester createJtagSubchainTester(String jtagInBus, String jtagOutBus) { if (isProcessRunning()) { System.out.println("Error: JtagTester test device must be created before Verilog process is started."); return null; } VerilogJtagSubchainTester tester = new VerilogJtagSubchainTester(this, jtagInBus, jtagOutBus); jtagTesters.add(tester); return tester; } 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 Verilog process is started."); return null; } VerilogJtagSubchainTester tester = new VerilogJtagSubchainTester(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 Verilog process is started."); return null; } VerilogLogicSettable ls = new VerilogLogicSettable(this, portName); logicSettables.add(ls); aliased.add(ls.getAliasedNames()); return ls; } public LogicSettable createLogicSettable(List portNames) { if (isProcessRunning()) { System.out.println("Error: LogicSettable test device must be created before Verilog process is started."); return null; } VerilogLogicSettable ls = new VerilogLogicSettable(this, portNames); logicSettables.add(ls); aliased.add(ls.getAliasedNames()); return ls; } public void disableNode(String node) { Exception e = new Exception("Unsupported feature"); e.printStackTrace(System.out); } public void enableNode(String node) { Exception e = new Exception("Unsupported feature"); e.printStackTrace(System.out); } public double getVdd() { return 1; } public double getSimulationTime() { return ticks; } /** * Start the Verilog process. Returns false if failed to start. * This performs three steps: * <P>1. Create a local copy of the test harness file with information specific to this chip, and test setup. * <P>2. Setup input/output streams for talking to/from the verilog process. * <P>3. Run the verilog process on the local test harness file. * @param verilogCommand the command to run Verilog. * @param verilogSource the chip netlist. * @param recordSim true to record simulation via $recordfile(), $recordvars; false to not do so. * @return true if verilog process started, false otherwise. */ public boolean start_(String verilogCommand, String verilogSource, int recordSim) { // parse the verilog source file: find the definition of the top level // module so we can instantiate it in the test harness VerilogParser vp = new VerilogParser(); if (!vp.parse(verilogSource)) { System.out.println("Failed parsing verilog source file "+verilogSource); return false; } // find the last module, and it's port definitions List modules = vp.getModules(); VerilogParser.Module top = (VerilogParser.Module)modules.get(modules.size()-1); // setup the local test harness file if (!startProcess(verilogCommand+" -s "+verilogSource, null, null, verilogSource+".log")) return false; // set up waveform file switch (recordSim) { case RECORDVARS: { // recordfile causes verilog to exit -- not supported anymore? issueCommand("$recordfile(\""+top.name+"\");"); issueCommand("$recordvars;"); //vout.write("$shm_open(\""+top.name+"\");\n"); //vout.write("$shm_probe(\"S\");\n"); //issueCommand("FID=$fopen(\""+top.name+".journal\");\n"); break; } case DUMPVARS: { issueCommand("$dumpfile(\""+top.name+".dump\");"); issueCommand("$dumpvars;"); //issueCommand("FID=$fopen(\""+top.name+".journal\");\n"); break; } case NORECORD: { break; } } // initialize all testers for (Iterator it = jtagTesters.iterator(); it.hasNext(); ) { BypassJtagTester tester = (BypassJtagTester)it.next(); tester.reset(); } return true; } /** * Let the verilog simulation run for some period of time. * Currently, one second = 1 million verilog ticks. Use * waitTicks() if you want to specify the number of verilog * ticks. * @param seconds */ public void wait(float seconds) { long ticks = (long)(seconds * 1e6); waitTicks(ticks); } public void waitNS(double ns) { long ticks = (long)(ns * 1e3); waitTicks(ticks); } public void waitPS(double ps) { waitTicks((long)ps); } /** * Let the verilog simulation for some number of verilog ticks. * @param ticks */ public void waitTicks(long ticks) { issueCommand("$db_steptime("+ticks+");"); this.ticks += ticks; } public double getTimeNS() { return ticks/1000.0; } /** * 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) { if (state != 0 && state != 1) { System.out.println("Illegal state passed to setNodeState: "+state+". Expected 0 or 1."); return; } // replace any disallowed characters node = node.replaceAll("@", "_"); issueCommand("force "+node+" = "+state+";"); StringBuffer ret = getLastCommandOutput(); if (ret.toString().matches("(Error!).*?(not declared)")) System.out.println("Error! setNodeState: node "+node+" not found"); } /** * 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. * May return -2 if node is undefined (X) or HiZ (Z). * Note this only works for single bit nodes, not busses * @param node the hierarchical spice node name */ public int getNodeState(String node) { // replace any disallowed characters node = node.replaceAll("@", "_"); issueCommand("$showvars("+node+");"); StringBuffer result = getLastCommandOutput(); String [] results = result.toString().trim().split("\n"); for (int i=0; i<results.length; i++) { String s = results[i]; String [] parts = s.split("\\s+"); // first str is node name (with hierarchy stripped away) if (node.endsWith(parts[0])) { // state is last str String val = parts[parts.length-1].toLowerCase(); if (val.equals("0") || val.equals("st0") || val.equals("su0") || val.equals("we0")) return 0; else if (val.equals("1") || val.equals("st1") || val.equals("su1") || val.equals("we1")) return 1; else if (val.equals("scheduled")) { System.out.println("Warning, attempting to read node '"+node+"' that has a scheduled event at time "+ticks+", you should advance time before reading the node"); return -2; } else return -2; // undefined or HiZ } } System.out.println("Error! getNodeState: node "+node+" not found"); return -1; } public void releaseNode(String s) { // replace any disallowed characters s = s.replaceAll("@", "_"); issueCommand("release "+s+";"); } public void releaseNodes(List nodes) { for (Iterator it = nodes.iterator(); it.hasNext(); ) { Object o = it.next(); String s = (String)o; releaseNode(s); } } /** * Provides an example Verilog 'chip' file for Unit Tests. * @return the path and file name of the file. Exits the program if not found. */ static String getExampleVerilogChipFile() { URL jtagController = VerilogModel.class.getResource(jtagControllerFile); if (jtagController == null) { System.out.println("Can't find resource "+jtagControllerFile+". Test Failed."); Infrastructure.exit(1); } return jtagController.getFile(); } /** * Signals cannot be explicitly tied together in verilog. Rather, they * must be tied together by only using the same name in all places the two names * were once used. The AliasedNames class maintains the relationship for a * single name tying together several ports, this class maintains all the * AliasedNames for a design. */ static class AllAliasedNames { private List aliases; private AllAliasedNames() { aliases = new ArrayList(); } private void add(AliasedNames names) { if (names == null) return; aliases.add(names); } String getAliasFor(String replacedName) { for (Iterator it = aliases.iterator(); it.hasNext(); ) { AliasedNames aliased = (AliasedNames)it.next(); String alias = aliased.getAliasFor(replacedName); if (alias != null) { return alias; } } return replacedName; } } /** * Signals cannot be explicitly tied together in verilog. Rather, they * must be tied together by using the same name in all places the two names * were once used. This class maintains which names have been replaced by * a single name. */ static class AliasedNames { private final String alias; private final List replacedNames; AliasedNames(String name, List replacedNames) { this.alias = name; this.replacedNames = new ArrayList(replacedNames); } private String getAliasFor(String n) { if (replacedNames.contains(n)) return alias; return null; } public String toString() { StringBuffer buf = new StringBuffer(); buf.append("Alias '"+alias+"' for: "); for (Iterator it = replacedNames.iterator(); it.hasNext(); ) { String nn = (String)it.next(); buf.append(nn); if (it.hasNext()) buf.append(", "); } return buf.toString(); } } static String formatDataNetName(String dataNetName) { // need to convert []'s in instance names to _ (but not brackets in net names) // this converts brackets in inst names with unconnected nets, which electric netlists as instname[n]_portname //dataNetName = dataNetName.replaceAll("\\[(.*?)\\]_","_$1__"); // this converts brackets in inst names in the hierarchy, so inst[n].morestuff -> inst_n_.morestuff //dataNetName = dataNetName.replaceAll("\\[(.*?)\\]\\.","_$1_."); // replace [X] with _X_ if X is non-numeric dataNetName = dataNetName.replaceAll("\\[([a-zA-Z_][^\\]]*)\\]", "_$1_"); // replace [X] with _X_ if it is not the last index dataNetName = dataNetName.replaceAll("\\[(.*?)\\](?=.)", "_$1_"); // also, need to get rid of @ dataNetName = dataNetName.replace('@', '_'); // convert spice-like name to verilog name String [] parts = dataNetName.split("\\."); StringBuffer newName = new StringBuffer(); for (int i=0; i<parts.length-1; i++) { if (parts[i].startsWith("x") || parts[i].startsWith("X")) { parts[i] = parts[i].substring(1); } newName.append(parts[i]); newName.append("."); } if (parts.length>0) newName.append(parts[parts.length-1]); return newName.toString(); } /** Unit test */ public static void main(String[] args) { // start process VerilogModel vm = new VerilogModel(); vm.start("verilog", getExampleVerilogChipFile(), VerilogModel.NORECORD); // send commands to process stdin vm.issueCommand("$showvars;"); vm.finish(); } }