/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: ChainControl.java * Written by Eric Kim and Tom O'Neill, Sun Microsystems. * * Copyright (c) 2004, 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.test; import javax.swing.tree.TreePath; import javax.swing.event.TreeModelListener; import javax.swing.event.TreeModelEvent; import javax.swing.tree.TreeModel; import java.util.ArrayList; /** * Main API for scan chain programming, provides control of a single port on a * JTAG tester. The scan chain hieararchy is ("system" node) -> * <code>ChipNode</code>-><code>ChainNode</code>-> * <code>SubchainNode</code>-><code>SubchainNode</code> ..., where there * can be multiple <code>ChainNode</code> entries per <code>ChipNode</code> * and multiple <code>SubchainNode</code> entries per <code>ChainNode</code> * or <code>SubchainNode</code>. The system node is a <code>TestNode</code> * object. The division of a scan chain into sub chains is for convenience in * accessing particular scan chain elements, and does not necessarily correspond * to any physical hierarchy on the chip. * <p> * To program a scan chain, one uses a {@link ChainControl#setInBits} *  method to specify new values for the chain elements. Scan chain values * can be set using {@link BitVector} objects or strings. Strings are * appropriate for simple set commands, but the {@link BitVector} class * supports more sophisticated manipulations. As a starting point for * {@link BitVector} manipulations, one may wish to use the method * {@link ChainControl#getInBits} to retrieve the current value of the bit * sequence to scan in to the chips. * <p> * By convention, the first bit in a {@link BitVector} or character in a * string always represents the last bit scanned into or out of the chip. Thus * 1) the bit and character indices match the position of the corresponding scan * chain element along the s_in chain, 2) the strings match the left-to-right * order in which scan chain elements appear in most schematics, and 3) the * order is consistent with the order of scan chain nodes in the XML file. * <p> * Also by convention, the node path names used in this class start at the chip * node (e.g., "<tt>miniHeater.pScan.row</tt> "). I.e., they exclude the * system node. * <p> * The class also provides convenience methods like * {@link ChainControl#getChips}, which returns an array of the chips in the * system, and {@link ChainControl#getChainPaths}, which returns an array of * the chains in a single chip or in the entire system. * <p> * Here is an example of how to create a <code>ChainControl</code> object: * <BLOCKQUOTE><TT>JtagTester jtag = new Netscan4(JTAG_IP_ADDRESS, * JTAG_TAP_NUMBER); <BR> * ChainControl control = new ChainControl(XML_PATH, jtag, DEFAULT_VDD, * DEFAULT_TCK_KHZ); <BR> * </TT> </BLOCKQUOTE> The <TT>JTAG_TAP_NUMBER</TT> parameter must be omitted * when using the {@link Netscan} or {@link MockJtag}  constructors. * The user can then call {@link #setInBits} and {@link #shift} to * program scan chains. */ public class ChainControl extends Logger { /** The root of the scan chain hierarchy. */ protected TestNode system; /** The JTAG tester object, if any. JtagTester is device-independent. */ final JtagTester jtag; /** * Nominal frequency for JTAG TCK, in kHz. E.g., this is the value ChainTest * returns to after generating a Schmoo plot. */ private int jtagKhz; /** * Nominal JTAG TAP voltage for chip power, in Volts. Also the default value * of Vdd. E.g., this is the value {@link ChainTest} returns to after * generating a Schmoo plot. */ private float jtagVolts; /** * Default action when the bits shifted out of the IR register are bad. * Initial value is {@link Infrastructure#SEVERITY_FATAL}. */ public int irBadSeverity = Infrastructure.SEVERITY_FATAL; /** * Default action when no consistency check is possible on bits shifted out * of the data register. Initial value is * {@link Infrastructure#SEVERITY_WARNING}. */ public int noTestSeverity = Infrastructure.SEVERITY_WARNING; /** * Default action when the bits shifted out of the data register are * inconsistent with expectation, see {@link #getExpectedBits}. Initial * value is {@link Infrastructure#SEVERITY_FATAL}. */ public int errTestSeverity = Infrastructure.SEVERITY_FATAL; /** * XML file read in */ public String xmlFile = null; /** * Creates a new instance of ChainControl, with the scan chain hierarchy * specified in the XML file <code>fileName</code>. For more information * on the XML file, see <tt>ChainG.dtd</tt> and ``The Scan Chain XML File * Format'' by Tom O'Neill. * * @param fileName * Name of XML file containing scan chain description */ ChainControl(String fileName) { openFile(fileName); jtag = null; } /** * Creates an object to program scan chains using the boundary scan * controller <code>jtagTester</code> and assuming the scan chain * hierarchy specified in the XML file <code>fileName</code>. For more * information on the XML file, see <tt>ChainG.dtd</tt> and <A * HREF="http://archivist/index.jsp?id=2004-1091"> "The Scan Chain XML File * Format" </A>. * <p> * Configures the provided JTAG tester to the TAP voltage * <code>jtagVolts</code>, frequency <code>jtagKhz</code>, and the * default stop state {@link NetscanGeneric#DEFAULT_STOP_STATE}. The * TRSTbar signal is also set <tt>LO</tt> briefly to reset the JTAG * controller for each chip on this JTAG tester. * <p> * The <code>jtagVolts</code> and <code>jtagKhz</code> parameters are * also used sometimes as the default Vdd and JTAG frequency values. E.g., * after generating a Schmoo plot, {@link ChainTest} leaves Vdd at * <code>jtagVolts</code> and the JTAG tester at <code>jtagKhz</code>. * Nevertheless, this constructor does not itself modify Vdd. * * @param fileName * Name of XML file containing scan chain description * @param jtagTester * JTAG tester object to use * @param jtagVolts * Nominal JTAG TAP voltage and default value of Vdd, in Volts. * @param jtagKhz * Nominal frequency for JTAG TCK, in kHz. */ public ChainControl(String fileName, JtagTester jtagTester, float jtagVolts, int jtagKhz) { System.out.print("Reading xml file "+fileName+"..."); System.out.flush(); long ctime = System.currentTimeMillis(); openFile(fileName); System.out.println("finished. Took "+Infrastructure.getElapsedTime(System.currentTimeMillis()-ctime)); jtag = jtagTester; this.jtagVolts = jtagVolts; this.jtagKhz = jtagKhz; jtagTester.configure(jtagVolts, jtagKhz); } /** * Get the system node * @return the system node that contains all other nodes */ public MyTreeNode getSystem() { return system; } /** Finalizer. Disconnects from JTAG tester if necessary */ protected void finalize() throws Throwable { super.finalize(); jtag.disconnect(); } /** * Returns device-independent JTAG tester object * * @return Device-independent JTAG tester object */ public JtagTester getJtag() { return jtag; } /** * Returns nominal voltage for JTAG TAP and chip power, in Volts * * @return Default JTAG TAP and chip V_DD, in Volts */ public float getJtagVolts() { return jtagVolts; } /** * Configures the JTAG tester to use the requested TAP voltage. This voltage * is also used as the default value of the chip Vdd (e.g., the value set * after completion of a Schmoo plot), but the routine does not modify the * current Vdd setting. * * @param defaultVdd * JTAG tester TAP voltage/nominal chip power, in Volts */ public void setJtagVolts(float defaultVdd) { this.jtagVolts = defaultVdd; jtag.configure(defaultVdd, jtagKhz); } /** * Returns nominal frequency for JTAG TCK, in kHz * * @return Nominal frequency for JTAG TCK, in kHz */ public int getJtagKhz() { return jtagKhz; } /** * Returns the xml file read by this chain control * * @return xml file read by this chain control */ public String getXmlFile() { return xmlFile; } /** * Configures the JTAG tester to use the requested TCK frequency. This * frequency is also used as the default frequency (e.g., the value set * after completion of a Schmoo plot). * * @param defaultKHz * Nominal frequency for JTAG TCK, in kHz */ public void setJtagKhz(int defaultKHz) { this.jtagKhz = defaultKHz; jtag.configure(jtagVolts, defaultKHz); } /** * Get the pin name for the selected <code>SubchainNode</code> object. * Currently the pin name is only used by {@link SamplerControl}. * * @param path * path name to the desired <code>SubchainNode</code> object * @return name of I/O pad associated with the selected subchain */ public String getSubchainPin(String path) { SubchainNode chainNode = (SubchainNode) this.findNode(path, SubchainNode.class); return chainNode.pin; } /** * Set the pin name for the selected <code>SubchainNode</code> object. * Currently the pin name is only used by {@link SamplerControl}. * * @param path * path name to the desired <code>SubchainNode</code> object * @param pin * name of I/O pad associated with the selected subchain */ public void setSubchainPin(String path, String pin) { SubchainNode chainNode = (SubchainNode) this.findNode(path, SubchainNode.class); chainNode.pin = pin; } /** * Return the number of scan chain elements within a given (subchain) node * in the scan chain. * * @param path * path name to the desired node, starting at the chip node * @return number of scan chain elements in node and its descendents */ public int getLength(String path) { SubchainNode chainNode = (SubchainNode) this.findNode(path, SubchainNode.class); return chainNode.getLength(); } /** * Return a copy of the scan chain bit pattern that will be written to * specified node on chip during the next {@link ChainControl#shift} *  call. The first element of the bit vector represents the scan chain * element that is scanned in last. * * @param path * path name to the desired node, starting at the chip node * @return Bit vector with pending bit pattern for current node */ public BitVector getInBits(String path) { SubchainNode chainNode = (SubchainNode) this.findNode(path, SubchainNode.class); return chainNode.getInBits(); } /** * Return a copy of the scan chain bit pattern that was written to specified * node on chip after the last {@link ChainControl#shift} call. The * first element of the bit vector represents the scan chain element that is * scanned out last. * * @param path * path name to the desired node, starting at the chip node * @return Bit vector with received bit pattern for current node */ public BitVector getOutBits(String path) { SubchainNode chainNode = (SubchainNode) findNode(path, SubchainNode.class); return chainNode.getOutBits(); } /** * Return a copy of the scan chain bit pattern that the library expected to * be shifted out of the specified node during the <em>previous</em> call * to {@link #shift} for the parent chain. This is useful for tracking * down problems when the scanned out bits don't equal the expectation. Bits * that are in the invalid state correspond to chain elements for which no * prediction was possible. * <p> * (Expert users: the method returns an archival copy of the state of * <code>outBitsExpected</code> during the previous consistency check.) * * @param path * path name to the desired node, starting at the chip node * @return Bit vector with expected bit pattern for current node */ public BitVector getExpectedBits(String path) { SubchainNode chainNode = (SubchainNode) findNode(path, SubchainNode.class); return chainNode.getOldOutBitsExpected(); } /** * Set scan chain bit pattern that will be written to specified node on chip * after the next shift() call. The first element of the bit vector * represents the scan chain element that is scanned in last. * * @param path * path name to the desired node, starting at the chip node * @param newBits * Bit array containing new settings */ public void setInBits(String path, BitVector newBits) { SubchainNode chainNode = (SubchainNode) this.findNode(path, SubchainNode.class); chainNode.setInBits(newBits); } /** * Set scan chain bit pattern that will be written to specified node on chip * after the next shift() call. The first character in the string represents * the scan chain element that is scanned in last. * * @param path * path name to the desired node, starting at the chip node * @param newBits * Character string containing new settings (e.g., "111001") */ public void setInBits(String path, String newBits) { SubchainNode chainNode = (SubchainNode) findNode(path, SubchainNode.class); chainNode.setInBits(newBits); } /** * Set scan chain bit pattern that will be written to specified node on chip * after the next shift() call. All elements receive value newValue. * * @param chainPath * path name to the desired node, starting at the chip node * @param newValue * new bit value to set each scan chain element to */ public void setInBits(String chainPath, boolean newValue) { int length = getLength(chainPath); BitVector bits = new BitVector(length, "setInBits()-bits"); bits.set(0, length, newValue); setInBits(chainPath, bits); } /** * For each scan chain in the system (on every chip), set the bit pattern * that will be written during the next shift() call to zero, or to * the clears state if clearable. */ public void resetInBits() { resetInBits(true); } /** * For each scan chain in the system (on every chip), set the bit pattern * that will be written during the next shift() call to zero. * @param useMasterClearState sets the bit to the clears state instead of * zero if clearable. */ public void resetInBits(boolean useMasterClearState) { String[] roots = getChainPaths(); for (int iroot = 0; iroot < roots.length; iroot++) { ChainNode chainRoot = (ChainNode) findNode(roots[iroot], ChainNode.class); chainRoot.resetInBits(useMasterClearState); } } /** * Inform the test library about a change in the state of the master clear * signal on the specified chip. In particular, appropriately modifies the * <tt>outBitsExpected</tt> (cf. {@link #getExpectedBits}) bit for any * scan chain element that has a <tt>clears</tt> value of "<tt>H</tt>", " * <tt>L</tt>", or "<tt>?</tt>" in the scan chain XML file. For more * information on the <tt>clears</tt> parameter, see <A * HREF="http://archivist/index.jsp?id=2004-1091"> "The Scan Chain XML File * Format" </A>. Call this method on any change in the master clear state, * or you may get false complaints about data shifted out not equalling * expected results. * * @param chipName * chip name */ public void processMasterClear(String chipName) { String[] roots = getChainPaths(chipName); for (int iroot = 0; iroot < roots.length; iroot++) { ChainNode root = (ChainNode) findNode(roots[iroot], ChainNode.class); root.processMasterClear(); } } /** * Invalidate expected scan chain element values for every scan chain on the * chip. Call this after a period at a low voltage, or you may get false * complaints about data shifted out not equalling expected results. * * @param chipName * chip name */ public void invalidate(String chipName) { String[] roots = getChainPaths(chipName); for (int iroot = 0; iroot < roots.length; iroot++) { ChainNode root = (ChainNode) findNode(roots[iroot], ChainNode.class); root.invalidate(); } } /** * Shift <code>inBits</code> (cf. {@link #setInBits}) into a root scan * chain on the chip. Like {@link #shift(String, boolean, boolean)}, except * that the response to the possible error conditions are specified * explicitly using the Infrastructure.SEVERITY_* constants. The bits that * are shifted out (see {@link #getOutBits}) are compared with expectation * (see {@link #getExpectedBits}). * * @param chainRoot * path name to the root scan chain, starting at the chip node * @param readEnable * true to enable reading from the scan chain latches. * @param writeEnable * true to enable writing to the scan chain latches. * @param irBadSeverity * action when bits scanned out of the instruction register are * wrong * @param noTestSeverity * action when no consistency check is possible * @param errTestSeverity * action when consistency check on scan chain functioning fails * @return true if the bits scanned out equal their expected values * @see Infrastructure#SEVERITY_NOMESSAGE * @see Infrastructure#SEVERITY_WARNING * @see Infrastructure#SEVERITY_NONFATAL * @see Infrastructure#SEVERITY_FATAL */ public boolean shift(String chainRoot, boolean readEnable, boolean writeEnable, int irBadSeverity, int noTestSeverity, int errTestSeverity) { MyTreeNode node = findNode(chainRoot); if (!ChainNode.class.isInstance(node)) { Infrastructure.fatal("Node '" + node + "' at path '" + chainRoot + "' is of class " + node.getClass().getName() + ", but shifts must be performed on members of class " + ChainNode.class.getName()); } boolean ret = ((ChainNode) node).shift(jtag, readEnable, writeEnable, irBadSeverity, noTestSeverity, errTestSeverity, this); return ret; } /** * Shift <code>inBits</code> (cf. {@link #setInBits}) into a root scan * chain on the chip. Like * {@link #shift(String, boolean, boolean, int, int, int)}, except the * response to the possible error conditions are specified the member * variables {@link #irBadSeverity},{@link #noTestSeverity}, and * {@link #errTestSeverity}. The bits that are shifted out (see * {@link #getOutBits}) are compared with expectation (see * {@link #getExpectedBits}). * * @param chainRoot * path name to the root scan chain, starting at the chip node * @param readEnable * true to enable reading from the scan chain latches. * @param writeEnable * true to enable writing to the scan chain latches. * @return true if the bits scanned out equal their expected values #see * irBadSeverity #see noTestSeverity #see errTestSeverity */ public boolean shift(String chainRoot, boolean readEnable, boolean writeEnable) { return shift(chainRoot, readEnable, writeEnable, irBadSeverity, noTestSeverity, errTestSeverity); } /** * Experts only: shift one bit of data from <code>inBits</code> (cf. * {@link #setInBits}) into a root scan chain on the chip. When this method * is used, the library does not attempt to keep track of the on-chip scan * chain states. Thus no consistency check is performed on the bit that is * shifted out, nor on the next shift. * * @param chainRoot * path name to the root scan chain, starting at the chip node * @param readEnable * true to enable reading from the scan chain latches. * @param writeEnable * true to enable writing to the scan chain latches. * @param irBadSeverity * action when bits scanned out of the instruction register are * wrong * @return the bit that got shifted out * @see Infrastructure#SEVERITY_NOMESSAGE * @see Infrastructure#SEVERITY_WARNING * @see Infrastructure#SEVERITY_NONFATAL * @see Infrastructure#SEVERITY_FATAL */ public boolean shiftOneBit(String chainRoot, boolean readEnable, boolean writeEnable, int irBadSeverity) { MyTreeNode node = findNode(chainRoot); if (!ChainNode.class.isInstance(node)) { Infrastructure.fatal("Node '" + node + "' at path '" + chainRoot + "' is of class " + node.getClass().getName() + ", but shifts must be performed on members of class " + ChainNode.class.getName()); } return ((ChainNode) node).shiftOneBit(jtag, readEnable, writeEnable, irBadSeverity, this); } /** * Returns path strings to all of the chips in the system * * @return path strings to the chips in the system */ public String[] getChips() { ArrayList chips = new ArrayList(); for (int i=0; i<system.getChildCount(); i++) { MyTreeNode child = system.getChildAt(i); if (child.getClass() == ChipNode.class) { chips.add(child.getPathString(1)); continue; } if (child.getName().equals(XMLIO.SCAN_CHAIN_DATA_NETS)) continue; // unknown node Infrastructure.nonfatal(child + " is a child of the system node, but is not a chip"); } String [] chips2 = new String[chips.size()]; for (int i=0; i<chips2.length; i++) { chips2[i] = (String)chips.get(i); } return chips2; } /** * Returns path strings to all of the root scan chains for the specified * chip. * * @param chipName * name of the chip * @return path strings to the root scan chains of the chip. */ public String[] getChainPaths(String chipName) { MyTreeNode chip = findNode(chipName, ChipNode.class); int nroot = chip.getChildCount(); String[] roots = new String[nroot]; for (int iroot = 0; iroot < nroot; iroot++) { MyTreeNode child = chip.getChildAt(iroot); if (!ChainNode.class.isInstance(child)) { Infrastructure.fatal(child +" (class="+child.getClass()+") is a child of chip " + chipName + ", but is not a ChainNode"); } else { roots[iroot] = child.getPathString(1); } } return roots; } /** * Returns path strings to all of the root scan chains in the system. * * @return path strings to the root scan chains in the system. */ public String[] getChainPaths() { int nrootTotal = 0; String[] chips = getChips(); for (int ichip = 0; ichip < chips.length; ichip++) { nrootTotal += getChainPaths(chips[ichip]).length; } int irootTotal = 0; String[] rootsTotal = new String[nrootTotal]; for (int ichip = 0; ichip < chips.length; ichip++) { String[] roots = getChainPaths(chips[ichip]); for (int iroot = 0; iroot < roots.length; iroot++) { rootsTotal[irootTotal] = roots[iroot]; irootTotal++; } } return rootsTotal; } /** * Returns path strings to all of the nodes below the specified node in the * scan chain hierarchy. * * @param path * name of the node to find descendents of * @return path strings to the nodes below the specified node */ public String[] getDescendents(String path) { MyTreeNode node = findNode(path); MyTreeNode[] nodes = node.getDescendents(); // Generate array of path strings, excluding the "system" level String[] paths = new String[nodes.length]; for (int ind = 0; ind < nodes.length; ind++) { paths[ind] = nodes[ind].getPathString(1); } return paths; } /** * Return the path to the scan chain that specified node is a sub-chain of * * @param path * path of sub-chain to find parent of * @return path to chain that the sub-chain is part of */ public String getParentChain(String path) { MyTreeNode node = findNode(path, SubchainNode.class); SubchainNode subchain = (SubchainNode) node; ChainNode chain = subchain.getParentChain(); return chain.getPathString(); } /** * Return the length of a single chip's instruction register * * @param chip * name of chip * @return length of chip's instruction register */ public int getLenIR(String chip) { ChipNode node = (ChipNode) findNode(chip, ChipNode.class); return node.getLengthIR(); } /** * Return the sum of the instruction register lengths for all of the chips * in the test system. * * @return sum of instruction register lengths for all chips */ public int getLenIR() { int sum = 0; String[] chips = getChips(); for (int ichip = 0; ichip < chips.length; ichip++) { sum += getLenIR(chips[ichip]); } return sum; } /** * Return the opcode of a root scan chain, with little endian bit ordering. * Bits 0-5 provide the address of the scan chain on the chip. Bits 6 and 7 * are write and read enable, respectively, but are overridden in software. * * @param chainRoot * name of the root scan chain to query * @return opcode of the selected root scan chain */ public String getOpcode(String chainRoot) { ChainNode node = (ChainNode) findNode(chainRoot, ChainNode.class); return node.getOpcode(); } /* * ###################################################################### * Here are some "power user" routines. If you need to use them, I haven't * done my job right above. * ###################################################################### */ /** * Find a node using the path relative to a specified root node. * * @param path * path name, starting below root * @param root * root an ancestor to the node being looked up * @return node at path path relative to root node root */ public MyTreeNode findNode(String path, MyTreeNode root) { if (path.equals("")) { return root; } MyTreeNode node = MyTreeNode.getNode(root, path); if (node == null) { Infrastructure.fatal("Can't find " + path + ". Hints: Paths start with a chip name. " + " Hierarchy levels are separated with '.'. " + "See chip XML file for correct paths."); } return node; } /** * Find a node using its full path, excluding the name of the system node. * * @param path * path name, starting at the chip node * @return node at path path relative to root node root */ public MyTreeNode findNode(String path) { return this.findNode(path, system); } /** * Find a node using its full path, excluding the name of the system node. * If the routine succeeds, the returned object may safely be cast to class * <code>expected</code>. * * @param path * path name, starting at the chip node * @param expected * class of node that is expected * @return node at path path relative to root node root */ public MyTreeNode findNode(String path, Class expected) { MyTreeNode node = findNode(path); if (!expected.isInstance(node)) { Infrastructure .fatal("Node at path " + path + " is of class " + node.getClass() + ", but was expecting class " + expected); } return node; } /** * Read the scan chain XML file, building the internal representation of the * scan chain tree */ void openFile(String name) { if (name == null) { return; } try { this.system = XMLIO.read(name); xmlFile = name; } catch (Exception e) { e.printStackTrace(); } } /** Unit test */ public static void main(String[] args) { ChainControl control = new ChainControl("heater.xml"); MyTreeNode node = control.findNode("heater.pScan"); System.out.println(node); MyTreeNode node2 = control.findNode("p0.column", node); System.out.println(node2); String[] chips = control.getChips(); for (int ind = 0; ind < chips.length; ind++) { System.out.println(ind + ": " + chips[ind] + ", lengthIR=" + control.getLenIR(chips[ind])); } System.out.println("Total IR length = " + control.getLenIR()); System.out.println("\nChains of heater:"); String[] roots = control.getChainPaths(); for (int ind = 0; ind < roots.length; ind++) { System.out.println(ind + ": " + roots[ind] + ", length=" + control.getLength(roots[ind])); } System.out.println("\nSubchains of heater:"); String[] subchains = control.getDescendents("heater"); for (int ind = 0; ind < subchains.length; ind++) { System.out.println(ind + ": " + subchains[ind]); } control.setSubchainPin("heater", "toad"); // Test the "can't shift to a subchain" message control.shift("heater.pScan.p0", false, false); } }