/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: ChainNode.java * Written by Eric Kim and Tom O'Neill, 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.util.List; import java.util.ArrayList; /** * Represents an entire scan chain, covering all of the scan chain elements at a * single address on the chip's JTAG controller. Sometimes called a "root" scan * chain, to emphasize distinction from a sub-chain. I/O to the chip occurs to * the entire root scan chain, rather than to individual * <code>SubchainNode</code>s. * <p> * * By convention, the first bit in a BitVector or 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> */ public class ChainNode extends SubchainNode { /** * Little-endian character string (e.g., "101010") representation of the * root scan chain's address at the JTAG controller. Bits 6 and 7 (the read * and write enable) are ignored during I/O with the chip. */ final private String opcode; /** * Scan chain bit pattern to be shifted into the chip during the next call * to this.shift(). */ protected BitVector inBits; /** * Scan chain bit pattern read back from the chip after last call to * this.shift(). Should only be modified by Netscan. */ protected BitVector outBits; /** * Expected value of outBits during the next call to this.shift(). Usually * this is just the previous value of inBits, but it can be modified by * Master Clears and reading from the chip. */ protected BitVector outBitsExpected; /** Expected value of outBits for the previous call to this.shift(). */ protected BitVector oldOutBitsExpected; /** * State of the scan chain elements' shadow register, for those that have * one. */ protected BitVector shadowState; /** Whether any data has been shifted to this scan chain */ private boolean initialized = false; /** * A list of shift listeners */ private List<ShiftListener> listeners; /** * Constructor for a root scan chain. * * @param name * node name * @param opcode * on-chip address of root scan chain * @param newLength * number of scan chain elements in node * @param comment * comment attached to this node */ public ChainNode(String name, String opcode, int newLength, String comment) { super(name, newLength, comment); this.opcode = opcode; listeners = new ArrayList<ShiftListener>(); createBitVectors(); } public String toString() { return super.toString() + " (op=" + opcode + ")"; } /** * Little-endian character string (e.g., "101010") representation of the * root scan chain's address at the JTAG controller. These are the low-order * 6 bits of the instruction register needed to access the chain. * * @return Address of root scan chain (little endian) */ String getOpcode() { return opcode; } /** * Set most recent bit sequence that was shifted out of the root scan chain * during this.shift(). Should only be used by * {@link NetscanGeneric#netScan_DR}. */ void setOutBits(BitVector newOutBits) { outBits.put(0, newOutBits); } /** * Get the scan chain bit pattern to be shifted into the chip during the next call * to this.shift(). * @return the in bits */ public BitVector getInBits() { return inBits; } /** * Get the scan chain bit pattern read back from the chip after last call to * this.shift(). Should only be modified by Netscan. * @return the out bits */ public BitVector getOutBits() { return outBits; } /** * Get expected value of outBits during the next call to this.shift(). Usually * this is just the previous value of inBits, but it can be modified by * Master Clears and reading from the chip. * @return the out bits expected */ public BitVector getOutBitsExpected() { return outBitsExpected; } /** * Get the expected value of outBits for the previous call to this.shift(). * @return the old out bits expected */ public BitVector getOldOutBitsExpected() { return oldOutBitsExpected; } /** * Get the state of the scan chain elements' shadow register, for those that have * one. * @return the shadow state bits */ public BitVector getShadowState() { return shadowState; } /** * This is for ChainG display * @return the outbits */ protected BitVector getOutBitsIndiscriminate() { return outBits; } /** * Returns ancestor <code>ChipNode</code>. * * @return Chip node that is the ancestor to this <code>ChainNode</code> */ ChipNode getParentChip() { MyTreeNode node = this; while (node.getClass() != ChipNode.class) { node = node.getParent(); if (node == null) { Infrastructure.fatal(node + " does not have a ChipNode as an ancestor"); } } return (ChipNode) node; } /** * Update shadowState data structure in response to a master clear. Call * this on a transition of master clear to <tt>HI</tt> _or_ <tt>LO</tt>, * or you may get false complaints about data shifted out not equalling * expected results. */ void processMasterClear() { for (int ind = 0; ind < getLength(); ind++) { SubchainNode node = findNodeAtIndex(ind); if (node.usesShadow() || node.usesDualPortedShadow()) { int clears = node.getClearBehavior(); // Note clearing has no effect on elements with CLEARS_NOT. // Otherwise sets the shadow register to HI, LO, or to an // unknown (invalid) state. if (clears == TestNode.CLEARS_LO) { shadowState.clear(ind); } else if (clears == TestNode.CLEARS_HI) { shadowState.set(ind); } else if (clears == TestNode.CLEARS_UNKNOWN) { shadowState.invalidate(ind); } } } } /** * Resets all inBits to zero, or to clears state if clearable and specified to do so * @param useMasterClearState true to reset to clears state if clearable. */ public void resetInBits(boolean useMasterClearState) { for (int i=0; i<getLength(); i++) { SubchainNode node = findNodeAtIndex(i); if (useMasterClearState && node.getClearBehavior() == TestNode.CLEARS_HI) inBits.set(i, true); else inBits.set(i, false); } } /** * Invalidate expected scan chain element values for this scan chain. Call * this after a period at a low voltage, or you may get false complaints * about data shifted out not equalling expected results. Sets * {@link ChainNode#initialized} to false to suppress "no bits being * compared" warning on next shift. */ void invalidate() { outBitsExpected.invalidate(); initialized = false; } /** * Shift data in this.inBits into the appropriate scan chain on the chip. * The previous data on the chip is shifted out into this.outBits, which is * then compared with expectation. * * @param jtag * JTAG tester object to perform shift with * @param readEnable * whether to set opcode's read-enable bit * @param writeEnable * whether to set opcode's write-enable bit * @param irBadSeverity * action when bits scanned out of IR are wrong * @param noTestSeverity * action when no consistency check is possible * @param errTestSeverity * action when consistency check fails * @param logger * Object with logging properties to use * * @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 */ boolean shift(JtagTester jtag, boolean readEnable, boolean writeEnable, int irBadSeverity, int noTestSeverity, int errTestSeverity, Logger logger) { logger.logOther("------ " + getPathString() + ", R=" + readEnable + ", W=" + writeEnable); jtag.shift(this, readEnable, writeEnable, irBadSeverity); boolean noErrors = checkOutBits(readEnable, noTestSeverity, errTestSeverity); // After shift, all elements should hold inBits oldOutBitsExpected.putIndiscriminate(0, outBitsExpected); outBitsExpected.put(0, inBits); // Update the expected bits from any shadow registers // Note: this implies that shadow registers are not modified by the chip if (readEnable) { for (int ind = 0; ind < getLength(); ind++) { SubchainNode node = findNodeAtIndex(ind); if (node.isReadable() && (node.usesShadow() && !node.usesDualPortedShadow())) { if (shadowState.isValid(ind)) { boolean state = shadowState.get(ind); outBitsExpected.set(ind, state); } } } } // Update the shadowState of any elements that wrote to their // shadow registers if (writeEnable) { for (int ind = 0; ind < getLength(); ind++) { SubchainNode node = findNodeAtIndex(ind); if (node.isWriteable() && (node.usesShadow() || node.usesDualPortedShadow())) { boolean state = inBits.get(ind); shadowState.set(ind, state); } } } shiftCompleted(); return noErrors; } /** * Shift one bit of data in this.inBits into the appropriate scan chain on * the chip. No consistency checking is performed on bit scanned out. This * is provided only for measuring chain length in ChainTest, and should not * otherwise be used. * * @param jtag * JTAG tester object to perform shift with * @param readEnable * whether to set opcode's read-enable bit * @param writeEnable * whether to set opcode's write-enable bit * @param irBadSeverity * action when bits scanned out of IR are wrong * @param logger * Object with logging properties to use * @return the bit that got shifted out * * @see Infrastructure#SEVERITY_NOMESSAGE * @see Infrastructure#SEVERITY_WARNING * @see Infrastructure#SEVERITY_NONFATAL * @see Infrastructure#SEVERITY_FATAL */ boolean shiftOneBit(JtagTester jtag, boolean readEnable, boolean writeEnable, int irBadSeverity, Logger logger) { logger.logOther("***** " + getPathString() + ", inBits=" + inBits); /* * Temporarily change chain length to 1. Note outBits could potentially * be clobbered after each length change (currently it isn't), so safest * to store intermediate outbit. */ int length = getLength(); setLength(1); jtag.shift(this, readEnable, writeEnable, irBadSeverity); boolean outbit = outBits.get(0); setLength(length); /* * Since this is only needed during development of the scan chain XML * file, it's not worth figuring out resulting state of chain */ outBitsExpected.invalidate(); oldOutBitsExpected.invalidate(); shadowState.invalidate(); return outbit; } /** * Called when the length of a node underneath this root node changes, * recomputes the root node's length. In addition, resizes the inBits and * outBits arrays to account for the change. Previous array contents are * overwritten with 0. Note method overrides that in parent. */ void lengthChanged() { // Recompute current node's length, taking children into account this.computeLength(); createBitVectors(); } /** * Find descendent of current <code>ChainNode</code> that contains the * scan chain element at index bitIndex. * * @param bitIndex * index of scan chain element * @return scan chain node containing the specified element. */ SubchainNode findNodeAtIndex(int bitIndex) { if (bitIndex < 0 || bitIndex > this.getLength()) { throw new IllegalArgumentException("bitIndex " + bitIndex + " not in allowed range 0.." + this.getLength()); } SubchainNode node = this; SubchainNode parent = null; for (int indChild=0, nodeIndex = 0;; indChild++) { // Find index in scan chain of next sibling node int nextIndex = nodeIndex + node.getLength(); if (nextIndex <= bitIndex) { // Target not within this node, accumlate length and try // next sibling nodeIndex = nextIndex; } else { // Within this node. If leaf node, we have found it. Else, // try first child. int nkids = node.getChildCount(); if (nkids == 0) { return node; } parent = node; indChild = 0; } node = (SubchainNode) parent.getChildAt(indChild); } } /* * Called by constructor or when length changed, replaces the BitVector * members with new versions of the correct length. */ protected void createBitVectors() { int newLength = getLength(); if (newLength < 0) newLength = 0; inBits = new BitVector(newLength, getPathString() + ".inBits"); outBitsExpected = new BitVector(newLength, getPathString() + ".outBitsExpected"); oldOutBitsExpected = new BitVector(newLength, getPathString() + ".oldOutBitsExpected"); outBits = new BitVector(newLength, getPathString() + ".outBits"); shadowState = new BitVector(newLength, getPathString() + ".shadowState"); } /** * Checks if the bits scanned out of the scan chain equal their expected * values. Optionally prints a warning to stderr if deviations are found. * * @param readEnable * whether readable chain elements read values in * @param noTestSeverity * action when no consistency check is possible * @param errTestSeverity * action when consistency check fails * @return true if no deviations found */ private boolean checkOutBits(boolean readEnable, int noTestSeverity, int errTestSeverity) { int length = getLength(); /* * All elements, except those that are unpredictable or have read a * value from another part of the chip, should now have the value in the * previous inBits */ for (int ind = 0; ind < length; ind++) { SubchainNode node = findNodeAtIndex(ind); if (node.isUnpredictable()) { outBitsExpected.invalidate(ind); } else if (readEnable && node.isReadable()) { /* * Can only know read value of an element with a shadow register * that is in a known state */ if (node.usesShadow() && shadowState.isValid(ind)) { outBitsExpected.set(ind, shadowState.get(ind)); } else { outBitsExpected.invalidate(ind); } } } // If no bits being compared, optionally print warning message if (outBitsExpected.isInvalid()) { if (initialized || noTestSeverity == Infrastructure.SEVERITY_FATAL || noTestSeverity == Infrastructure.SEVERITY_NONFATAL) { Infrastructure.error(noTestSeverity, getPathString() + ".shift() warning: no bits being compared, " + "see ${TEST_ROOT}/FAQ.html"); } } initialized = true; // Create a BitVector that has a bit set for every deviation BitVector errors = new BitVector(length, "checkOutBits()-errors"); for (int iBit = 0; iBit < length; iBit++) { if (outBitsExpected.isValid(iBit) && outBits.get(iBit) != outBitsExpected.get(iBit)) { errors.set(iBit); } else { errors.clear(iBit); } } boolean noErrors = errors.isEmpty(); if (noErrors == false) { Infrastructure.error(errTestSeverity, getPathString() + ".shift() error:\n expected: " + outBitsExpected + "\n outBits: " + outBits + "\nFor details, see the Appendix in 'Using the Test" + "\nSoftware Library' for details about this error."); } return noErrors; } int findRun(int indStart) { SubchainNode start = findNodeAtIndex(indStart); int clears = start.getClearBehavior(); boolean read = start.isReadable(); boolean write = start.isWriteable(); boolean shadow = start.usesShadow(); boolean unpredictable = start.isUnpredictable(); int ind; for (ind = indStart; ind < getLength(); ind++) { SubchainNode subchain = findNodeAtIndex(ind); if (subchain.getClearBehavior() != clears || subchain.isReadable() != read || subchain.isWriteable() != write || subchain.usesShadow() != shadow || subchain.isUnpredictable() != unpredictable) { return ind; } } return ind; } /** Helper for CompareXML */ void compare(ChainNode that, String thisFile, String thatFile) { // System.out.println("Differences for chain " + this); super.compare(that, thisFile, thatFile); int length = getLength(); if (that.getLength() != length) { System.out.println("**** Chain " + thisFile + ":" + this + " has length " + length + ", but " + thatFile + ":" + that + " has " + that.getLength() + ". Aborting comparison"); Infrastructure.exit(1); } int thisIndex = 0, thatIndex = 0; while (thisIndex < length && thatIndex < length) { SubchainNode thisSubchain = findNodeAtIndex(thisIndex); SubchainNode thatSubchain = that.findNodeAtIndex(thatIndex); int thisStartIndex = thisIndex; int thatStartIndex = thatIndex; thisIndex = findRun(thisIndex); thatIndex = that.findRun(thatIndex); // System.out.println("Comparing run from " + thisStartIndex // + " to " + thisIndex + ", starting at " // + thisSubchain.getPathString()); if ((thisIndex - thisStartIndex) != (thatIndex - thatStartIndex)) { System.out.println("**** " + thisFile + " has subchain run of length " + +(thisIndex - thisStartIndex) + " starting at subchain " + thisSubchain.getPathString() + ", but " + thatFile + " has run of length " + (thatIndex - thatStartIndex) + " starting at subchain " + thatSubchain.getPathString()); } String thisState = thisSubchain.getState(); String thatState = thatSubchain.getState(); if (thisState.equals(thatState) == false) { System.out.println("**** Subchain run starting at " + thisFile + ":" + thisSubchain.getPathString() + " has mode " + thisState + ", but run starting at " + thatFile + ": " + thatSubchain.getPathString() + " has mode " + thatState); } } } public static interface ShiftListener { public void shiftCompleted(ChainNode node); } public void addListener(ShiftListener l) { listeners.add(l); } public void removeListener(ShiftListener l) { listeners.remove(l); } private void shiftCompleted() { for (ShiftListener l : listeners) { l.shiftCompleted(this); } } public static void main(String[] args) { String filename, path; int index; if (args.length >= 3) { filename = args[0]; path = args[1]; index = Integer.parseInt(args[2]); } else { filename = "heater.xml"; path = "heater.pScan"; index = 40; } ChainModel cm = new ChainModel(filename); ChainNode node = (ChainNode) cm.findNode(path); SubchainNode found = node.findNodeAtIndex(index); System.out.println(found.getPathString()); } }