/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: ScanChainXML.java
*
* 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.io.output;
import com.sun.electric.database.hierarchy.*;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.PortInst;
import com.sun.electric.database.topology.ArcInst;
import com.sun.electric.database.topology.Connection;
import com.sun.electric.database.network.Netlist;
import com.sun.electric.database.network.Network;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.text.Name;
import java.util.*;
import java.io.*;
/**
* Class to define XML for scan chains.
*/
public class ScanChainXML {
private static final boolean DEBUG = false;
private static final boolean FLAT = false;
private static final boolean REDUCE = true;
private static Netlist.ShortResistors SHORT_RESISTORS = Netlist.ShortResistors.PARASITIC;
// --------------------------- Scan Chain Primitives -----------------------------
/** Defines a Scan Chain Element, which contains one bit of scan storage */
private static class ScanChainElement {
public final String name;
public final String access;
public final String clears;
public final String inport;
public final String outport;
public final DataNet dataport;
public final DataNet dataport2;
private ScanChainElement(String name, String access, String clears, String inport,
String outport, String dataport, String dataport2) {
this.name = name;
this.access = access;
this.clears = clears;
this.inport = inport;
this.outport = outport;
if (dataport == null || dataport.equals(""))
this.dataport = null;
else
this.dataport = new DataNet(dataport);
if (dataport2 == null || dataport2.equals(""))
this.dataport2 = null;
else
this.dataport2 = new DataNet(dataport2);
}
}
private static class DataNet {
public final String net;
public final String options; // options including parenthesis
/**
* Creates a data net object that describes a net that is written or read
* to by a scan chain element.
* @param netName the name of the data net, including any options prepended
* in parenthesis. R for readable, W for writeable, I for inverted.
* Ex: net26 or net26(R) or net26(RWI).
*/
public DataNet(String netName) {
int i = netName.indexOf('(');
if (i != -1) {
net = netName.substring(0, i);
options = netName.substring(i, netName.length());
} else {
net = netName;
options = "";
}
}
/**
* Creates a data net object that describes a net that is written or read
* to by a scan chain element.
* @param net the net name
* @param options options describing the network: R for readable, W for
* writable, I for inverted. Should be encased in parenthesis, such as
* (RW).
*/
public DataNet(String net, String options) {
this.net = net;
this.options = options;
}
public String toString() { return net+options; }
}
/** Defines the Jtag controller from which the scan chains start and end */
private static class JtagController {
public final String name;
public final int lengthIR;
private List<Port> ports;
protected static class Port {
public final int opcode;
public final String soutPort;
public final String chainName;
public Port(int opcode, String soutPort, String chainName) {
this.opcode = opcode;
this.soutPort = soutPort;
this.chainName = chainName;
}
}
private JtagController(String name, int lengthIR) {
this.name = name;
this.lengthIR = lengthIR;
ports = new ArrayList<Port>();
}
/**
* Add an opcode for a chain, and the associated scan out port,
* for example addPort(1, "leaf1[0]")
* @param opcode the opcode of the chain
* @param soutPort the associated scan out port
*/
private void addPort(int opcode, String soutPort, String chainName) {
Port p = new Port(opcode, soutPort, chainName);
ports.add(p);
}
/** get iterator over JtagContoller.Port list */
private Iterator<Port> getPorts() { return ports.iterator(); }
}
/**
* Define a pass through cell that passes scan data through it. Examples
* are inverters or buffers.
*/
private static class PassThroughCell {
public final String cellName;
public final String inport;
public final String outport;
public PassThroughCell(String cellName, String inport, String outport) {
this.cellName = cellName;
this.inport = inport;
this.outport = outport;
}
}
// --------------------------------------------------------------------
private String outputFile;
private PrintWriter out;
private File outFile;
// objects used to parse cell schematics
private JtagController jtagController;
private HashMap<String,ScanChainElement> scanChainElements;
private HashMap<String,PassThroughCell> passThroughCells;
private HashMap<Cell,Cell> cellsToFlatten;
private String chipName;
private Cell jtagCell;
// list of parsed objects to write to XML
private Map<String,Entity> entities; // list of !ENTITY definitions
private List<Chain> chains; // list of top level chains
private SubChain endChain;
// for tracing one subchain
private List<String> chainStartExports = null; // list of strings
private List<String> chainNames = null; // list of strings
private List<ExPort> chainStartExPorts = null; // list of ExPorts
// ------------------------ Constructors ------------------------------
/**
* Create a new ScanChainXML object that will parse the schematics and
* write out an XML description
*/
public ScanChainXML() {
this.jtagController = null;
this.scanChainElements = new HashMap<String,ScanChainElement>();
this.passThroughCells = new HashMap<String,PassThroughCell>();
this.cellsToFlatten = new HashMap<Cell,Cell>();
this.chipName = "?";
this.jtagCell = null;
outputFile = null;
out = new PrintWriter(System.out);
outFile = null;
entities = new HashMap<String,Entity>();
chains = new ArrayList<Chain>();
}
// --------------------------- Settings --------------------------------
/**
* Specify a scan chain element. When an instance of this is found, it is will
* be parsed as one bit in the scan chain.
* @param name name of the cell to be defined as a scan chain element.
* @param access the access type: for example, "RW".
* @param clears the clears type: for example, "L".
* @param inport the name of input data port, typically "sin".
* May contain index info, such as "s[1]"
* @param outport the name of the output data port, typically "sout".
* May contain index info, such as "ss[1]"
*/
public void addScanChainElement(String name, String access, String clears, String inport, String outport) {
ScanChainElement e = new ScanChainElement(name, access, clears, inport, outport, "", "");
scanChainElements.put(name+"_"+inport, e);
}
/**
* Specify a scan chain element. When an instance of this is found, it is will
* be parsed as one bit in the scan chain.
* @param name name of the cell to be defined as a scan chain element.
* @param access the access type: for example, "RW".
* @param clears the clears type: for example, "L".
* @param inport the name of input data port, typically "sin".
* May contain index info, such as "s[1]"
* @param outport the name of the output data port, typically "sout".
* May contain index info, such as "ss[1]"
* @param dataport the name of the port the scan data is read from and written to. May include options
* R, W, or I for (Readable,Writable,Inverted) in parenthesis at the end. Ex: dout(RW)
* @param dataport2 another port for data like dataport, with the same format.
*/
public void addScanChainElement(String name, String access, String clears, String inport, String outport, String dataport, String dataport2) {
ScanChainElement e = new ScanChainElement(name, access, clears, inport, outport, dataport, dataport2);
scanChainElements.put(name+"_"+inport, e);
}
/**
* Specify a pass through element. Pass through elements are found in series in
* the scan chain, but are not scan chain elements themselves. Examples of this are
* inverters and buffers that buffer the scan chain data.
* @param cellName name of the cell to be defined as a pass through element
* @param inport the name of the input port that passes data through
* May contain index info, such as "s[1]"
* @param outport the name of the output port that passes data through
* May contain index info, such as "ss[1]"
*/
public void addPassThroughCell(String cellName, String inport, String outport) {
PassThroughCell p = new PassThroughCell(cellName, inport, outport);
passThroughCells.put(cellName+"_"+inport, p);
}
/**
* Specify a cell to flatten. The XML is hierarchical, but sometimes you don't need
* or want all that hierarchy. This specifies a cell that will be flattened
* @param libName the library that contains the cell
* @param cellName the name of the cell
*/
public void addCellToFlatten(String libName, String cellName) {
//System.out.println("Warning: addCellToFlatten() optimization is no longer supported.");
Library lib = Library.findLibrary(libName);
if (lib == null) {
System.out.println("Did not find library "+libName+" for flattening cell "+cellName);
return;
}
Cell cell = lib.findNodeProto(cellName);
if (cell == null) {
System.out.println("Did not find cell "+cellName+" to flatten, in library "+libName);
return;
}
cellsToFlatten.put(cell, cell);
}
/**
* Specify the JTAG Controller. All scan chains are assumed to start, and end, at the
* JTAG Controller. This specifies the jtag controller.
* @param jtagLib the name of the library that holds the jtag controller cell
* @param jtagCellName the name of the cell that is the jtag controller
* @param lengthIR the number of instruction register bits in the jtag controller.
*/
public void setJtagController(String jtagLib, String jtagCellName, int lengthIR) {
Library lib = Library.findLibrary(jtagLib);
if (lib == null) {
System.out.println("Did not find jtag library "+jtagLib);
return;
}
Cell cell = lib.findNodeProto(jtagCellName);
if (cell == null) {
System.out.println("Did not find jtag cell "+jtagCellName+" in library "+jtagLib);
return;
}
jtagCell = cell;
jtagController = new JtagController(jtagCellName, lengthIR);
//PassThroughCell endCell = new PassThroughCell(jtagCellName, null, null);
endChain = new SubChain("end:jtagController", -1);
}
/**
* Add a port to the JTAG Controller that serves as a starting point for a scan chain.
* A JTAG Controller may have several ports that each have a scan chain attached.
* The JTAG Controller must have already been specified using setJtagController.
* @param opcode the opcode for this scan chain
* @param soutPortName the port name that outputs data for the scan chain.
* May contain index info, such as "leaf1[1]"
* @param chainName the name given to this scan chain
*/
public void addJtagPort(int opcode, String soutPortName, String chainName) {
if (jtagController == null) {
System.out.println("Can't add port "+soutPortName+" because the jtag controller has not been defined yet");
return;
}
jtagController.addPort(opcode, soutPortName, chainName);
}
/**
* Start tracing a chain from the specified export in the start cell. This
* is used to trace a section of the scan chain. This
* traces only one chain.
* @param exportName
* @param chainName
*/
public void startFromExport(String exportName, String chainName) {
if (chainStartExports == null) {
chainStartExports = new ArrayList<String>();
chainNames = new ArrayList<String>();
chainStartExPorts = new ArrayList<ExPort>();
}
chainStartExports.add(exportName);
chainNames.add(chainName);
}
/**
* Specify the name of the chip. Only used when writing the chip name to the file.
* @param name the chip name
*/
public void setChipName(String name) { chipName = name; }
/**
* Set the output file. This may be an absolute or relative path. If this
* option is not specified, the output goes to the Electric console.
* @param file the name of the file.
*/
public void setOutput(String file) {
// try to open outputFile
outputFile = file;
try {
outFile = new File(outputFile);
out = new PrintWriter(new BufferedWriter(new FileWriter(outFile)));
} catch (IOException e) {
System.out.println(e.getMessage() + "\nWriting XML to console");
}
}
/**
* Start tracing all the scan chains from the any instances of specified
* jtag controller
*/
public void start(String libName, String cellName) {
Library lib = Library.findLibrary(libName);
if (lib == null) {
System.out.println("Did not find library "+libName+" for starting chain analysis in cell "+cellName);
return;
}
Cell cell = lib.findNodeProto(cellName);
if (cell == null) {
System.out.println("Did not find cell "+cellName+" for starting chain analysis, in library "+libName);
return;
}
Stack<Nodable> startNode = null;
if (chainStartExports != null) {
for (int i=0; i<chainStartExports.size(); i++) {
ExPort startExport = getExPort(cell, chainStartExports.get(i));
if (startExport == null) {
System.out.println("Cannot find export "+chainStartExports.get(i)+" in cell "+cell.describe(false));
continue;
}
chainStartExPorts.add(startExport);
}
if (jtagController == null)
jtagController = new JtagController("", 8);
} else {
if (jtagCell == null) {
return;
}
startNode = findStartNode(cell, new Stack<Nodable>());
if (startNode == null) {
System.out.println("Did not find any usages of the jtag controller: "+jtagCell.getName());
return;
}
Nodable no = startNode.lastElement();
System.out.println("*** Generating chains starting from jtag controller "+no.getParent().describe(false)+" : "+no.getName());
}
start(startNode);
if (outFile != null) {
System.out.println("Wrote XML file to "+outFile.getAbsolutePath());
} else {
System.out.println("Wrote XML file to console");
}
}
// find the start node. it will be the last nodable in the var context
private Stack<Nodable> findStartNode(Cell cell, Stack<Nodable> context) {
Netlist netlist = cell.getNetlist(SHORT_RESISTORS);
for (Iterator<Nodable> it = netlist.getNodables(); it.hasNext(); ) {
Nodable no = it.next();
if (no.getProto().getName().equals(jtagCell.getName())) {
context.push(no);
return context;
}
if (no.isCellInstance()) {
// descend
Cell subCell = (Cell)no.getProto();
subCell = subCell.contentsView();
if (subCell == null) subCell = (Cell)no.getProto();
context.push(no);
Stack<Nodable> next = findStartNode(subCell, context);
if (next == null)
context.pop(); // get rid of no
else
return next;
}
}
return null;
}
/**
* Start tracing the scan chains. The nodeinst must be the jtag controller
* @param startNode The context pointing to the jtag controller.
*/
private void start(Stack<Nodable> startNode) {
if (startNode != null) {
// generate a chain for each port
for (Iterator<JtagController.Port> it = jtagController.getPorts(); it.hasNext(); ) {
JtagController.Port jtagPort = it.next();
if (jtagPort.soutPort == null) continue;
if (DEBUG) System.out.println("Starting chain "+jtagPort.opcode+" from port "+jtagPort.soutPort);
String chainName = jtagPort.chainName;
if (chainName == null)
chainName = "chain_"+jtagPort.soutPort;
Chain chain = new Chain(chainName, jtagPort.opcode, -1);
Stack<Nodable> startNodeCopy = new Stack<Nodable>();
startNodeCopy.addAll(startNode);
startChain(chain, startNodeCopy, jtagPort.soutPort);
// report number of elements found
int found = chain.numScanElements();
System.out.println("Info: completed successfully: chain "+chainName+" had "+found+" scan chain elements");
// make sure chain ended properly
SubChain last = chain.getLastSubChain();
if (last != endChain) {
System.out.println("Error! Chain "+chainName+" did not end at the jtag controller. Possible error in parsing or schematics.");
if (last != null) System.out.println(" Last sub chain is "+last.name+", length="+last.length);
}
chains.add(chain);
}
} else if (chainStartExPorts != null) {
for (int i=0; i<chainStartExPorts.size(); i++) {
ExPort startExport = chainStartExPorts.get(i);
String name = chainNames.get(i);
Chain chain = new Chain(name, 0, -1);
System.out.println("Tracing sub-chain "+name+" from export "+startExport);
appendChain(chain, getOtherPorts(startExport));
int found = chain.numScanElements();
System.out.println("Info: completed successfully: chain "+name+" had "+found+" scan chain elements");
chains.add(chain);
}
} else {
System.out.println("No starting point, aborting.");
return;
}
// post process
postProcessEntitiesRemovePassThroughs();
if (REDUCE)
postProcessEntitiesPhase1();
// write header
out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
out.println("\n<!--");
out.println(" Document : "+outputFile);
out.println(" Author : automatically generated by Electric");
out.println(" Description : none");
out.println("-->\n");
out.println();
out.println("<!DOCTYPE ChainG SYSTEM \"file:./ChainG.dtd\" [");
// print all entities
if (!FLAT) {
for (Entity ent : entities.values()) {
ent.writeDefinition(out, new StringBuffer(), cellsToFlatten);
}
out.println("]>");
out.println();
}
// print all chains
StringBuffer indent = new StringBuffer();
out.println("<ChainG>");
indent.append("\t");
out.println(indent+"<system>");
indent.append("\t");
out.println(indent+"<chip name=\""+chipName+"\" lengthIR=\""+jtagController.lengthIR+"\">");
indent.append("\t");
for (Chain chain : chains) {
if (chain.numScanElements() == 0) continue;
chain.write(out, indent, null, cellsToFlatten);
if (DEBUG) System.out.println("Double Check: chain "+chain.name+" had "+chain.numScanElements()+" scan chain elements");
}
indent.setLength(indent.length() - 1);
out.println(indent+"</chip>");
indent.setLength(indent.length() - 1);
out.println(indent+"</system>");
indent.setLength(indent.length() - 1);
out.println("</ChainG>");
out.flush();
}
// ------------------------- Elements ----------------------------------------
private static class SubChainInst {
private Port inport;
private Port outport;
private Nodable no;
private SubChain content;
private SubChainInst(Port inport, Port outport, Nodable no, SubChain content) {
this.inport = inport;
this.outport = outport;
this.no = no;
this.content = content;
}
protected void setInport(Port port) { this.inport = port; }
protected Port getInport() { return inport; }
protected void setOutport(Port port) { this.outport = port; }
protected Port getOutport() { return outport; }
public String toString() {
return "SubChainInst "+no.getName()+"; in: "+inport+", out: "+outport;
}
protected SubChain getSubChain() { return content; }
protected String getName() { return no.getName(); }
}
/**
* A chain is a description of an entire scan chain
*/
private static class Chain {
protected String name;
private int opcode; // the opcode to control this chain
protected int length; // not output if 0 or less
protected String access; // not output if null
protected String clears; // not output if null
protected DataNet dataNet; // network data is written to, not output if null
protected DataNet dataNet2; // network dataBar is written to, not output if null
private List<SubChainInst> subchains; // list of ScanChainInstances
private Chain(String name, int opcode, int length, String access, String clears,
DataNet dataNet, DataNet dataNet2) {
this.name = name;
this.opcode = opcode;
this.length = length;
this.access = access;
this.clears = clears;
this.dataNet = dataNet;
this.dataNet2 = dataNet2;
subchains = new ArrayList<SubChainInst>();
}
private Chain(String name, int opcode, int length) {
this(name, opcode, length, null, null, null, null);
}
protected void addSubChainInst(SubChainInst subChain) {
subchains.add(subChain);
}
protected int getSubChainSize() { return subchains.size(); }
protected SubChainInst getSubChainInst(int i) { return subchains.get(i); }
protected SubChain getSubChain(int i) {
SubChainInst inst = subchains.get(i);
if (inst == null) return null;
return inst.getSubChain();
}
protected Iterator<SubChainInst> getSubChainInsts() { return subchains.iterator(); }
protected Iterator<SubChain> getSubChains() {
List<SubChain> subs = new ArrayList<SubChain>();
for (SubChainInst inst : subchains) {
subs.add(inst.getSubChain());
}
return subs.iterator();
}
protected int getLength() { return length; }
protected String getAccess() { return access; }
protected String getClears() { return clears; }
protected void write(PrintWriter out, StringBuffer indent, String instName, Map<Cell,Cell> cellsToFlatten) {
if (numScanElements() == 0) return; // nothing to print
String n = (instName == null) ? name : instName;
out.print(indent+"<"+getTag()+" name=\""+n+"\"");
if (opcode > -1) out.print(" opcode=\""+Integer.toBinaryString(opcode)+"\"");
if (length > 0) out.print(" length=\""+length+"\"");
if (access != null) out.print(" access=\""+access+"\"");
if (clears != null) out.print(" clears=\""+clears+"\"");
if (dataNet != null) out.print(" dataNet=\""+dataNet+"\"");
if (dataNet2 != null) out.print(" dataNet2=\""+dataNet2+"\"");
// short hand if no elements
if (subchains.size() == 0) {
out.println(" />");
return;
}
out.println(">");
indent.append("\t");
for (Iterator<SubChainInst> it = getSubChainInsts(); it.hasNext(); ) {
SubChainInst inst = it.next();
SubChain subChain = inst.getSubChain();
subChain.write(out, indent, inst.getName(), cellsToFlatten);
}
indent.setLength(indent.length() - 1);
out.println(indent+"</"+getTag()+">");
}
protected String getTag() { return "chain"; }
protected int numScanElements() {
int num = 0;
if (length > 0) num += length;
for (SubChainInst inst : subchains) {
SubChain sub = inst.getSubChain();
num += sub.numScanElements();
}
//System.out.println("Num scan chain elements in "+name+": "+num+" (length="+length+")");
return num;
}
/**
* Get the last subchain in this chain
* @return
*/
protected SubChain getLastSubChain() {
if (subchains.size() == 0) return null;
SubChainInst last = subchains.get(subchains.size() - 1);
return last.getSubChain().getLastSubChain();
}
protected void copyTo(Chain dest) {
dest.name = this.name;
dest.length = this.length;
dest.access = this.access;
dest.clears = this.clears;
dest.subchains.clear();
dest.subchains.addAll(this.subchains);
}
protected void removePassThroughs() {
List<SubChainInst> toRemove = new ArrayList<SubChainInst>();
for (Iterator<SubChainInst> it2 = getSubChainInsts(); it2.hasNext(); ) {
SubChainInst inst = it2.next();
SubChain sub = inst.getSubChain();
if (sub.isPassThrough()) {
//System.out.println("Removed pass through cell "+sub.name);
toRemove.add(inst);
}
}
for (SubChainInst sci : toRemove) {
subchains.remove(sci);
}
}
protected void replaceSubChainInsts(List<SubChainInst> newSubChainInsts) {
subchains.clear();
subchains.addAll(newSubChainInsts);
}
protected void remove(SubChainInst inst) {
subchains.remove(inst);
}
protected List<SubChainInst> getSubChainsInsts() {
ArrayList<SubChainInst> copy = new ArrayList<SubChainInst>();
for (Iterator<SubChainInst> it = getSubChainInsts(); it.hasNext(); ) copy.add(it.next());
return copy;
}
protected void addAllSubChainInsts(int i, List<SubChainInst> list) {
subchains.addAll(i, list);
}
}
/**
* A subchain is exactly like a chain, except that it must be part of
* a chain, and cannot stand alone.
*/
private static class SubChain extends Chain {
private boolean passThrough;
private SubChain(String name, int length, String access, String clears, DataNet dataNet, DataNet dataNet2) {
super(name, -1, length, access, clears, dataNet, dataNet2);
passThrough = false;
}
private SubChain(String name, int length) {
super(name, -1, length);
}
protected String getTag() { return "subchain"; }
private void setPassThrough(boolean b) { passThrough = b; }
private boolean isPassThrough() { return passThrough; }
/**
* Get the last subchain in this chain
* @return
*/
protected SubChain getLastSubChain() {
if (getSubChainSize() == 0) {
return this;
}
return super.getLastSubChain();
}
public Object clone() {
SubChain sub = new SubChain(name, length, access, clears, dataNet, dataNet2);
this.copyTo(sub);
sub.passThrough = passThrough;
return sub;
}
}
/**
* An entity is a SubChain that associate with a cell, and contains
* the scan chain elements for that cell. This is unlike a subchain,
* which may simply be a single scan element, or a group of scan elements.
* Entities also serve as caching mechanisms, and define the chain for a cell.
*/
private static class Entity extends SubChain {
private static final String deftag = "!ENTITY";
private Cell cell;
private ExPort inExport;
private ExPort outExport;
private Entity(Cell cell) {
super(cell.getLibrary().getName()+"_"+cell.getName(), -1);
this.cell = cell;
}
private void setInExPort(ExPort port) { this.inExport = port; }
// private ExPort getInExPort() { return inExport; }
private void setOutExPort(ExPort port) { this.outExport = port; }
private ExPort getOutExPort() { return outExport; }
protected void writeDefinition(PrintWriter out, StringBuffer indent, Map<Cell,Cell> cellsToFlatten) {
if (FLAT) return;
// don't write a definition
if (numScanElements() < 3 || getSubChainSize() == 0) return;
out.println(indent+"<"+deftag+" "+getKey()+" '");
indent.append("\t");
for (Iterator<SubChainInst> it = getSubChainInsts(); it.hasNext(); ) {
SubChainInst inst = it.next();
SubChain subChain = inst.getSubChain();
subChain.write(out, indent, inst.getName(), cellsToFlatten);
}
indent.setLength(indent.length() - 1);
out.println(indent+"'>");
}
protected void write(PrintWriter out, StringBuffer indent, String instName, Map<Cell,Cell> cellsToFlatten) {
if (FLAT) {
super.write(out, indent, instName, cellsToFlatten);
return;
}
if (numScanElements() < 3 || getSubChainSize() == 0) {
super.write(out, indent, instName, cellsToFlatten);
} else {
//super.write(out, indent);
out.println(indent+"<subchain name=\""+instName+"\"> &"+getKey()+"; </subchain>");
}
}
private Object getKey() {
String key = name+"_"+inExport.name.toString();
key = key.replaceAll("[\\[\\]]", "_");
return key;
}
public Object clone() {
Entity ent = new Entity(cell);
this.copyTo(ent);
ent.inExport = this.inExport;
ent.outExport = this.outExport;
return ent;
}
}
/**
* A Port holds information about a single port, that may be part of a bussed portinst
*/
private static class Port {
private PortProto pp;
private int index;
private Name name;
private Nodable no;
private Port(Name name, Nodable no, PortProto pp, int index) {
this.name = name;
this.no = no;
this.pp = pp;
this.index = index;
}
public String toString() { if (name == null) return null; return no.getName() +":"+name.toString(); }
public void print() {
System.out.println(" Name: "+name);
System.out.println(" No: "+no);
System.out.println(" int: "+index);
System.out.println(" pp: "+pp);
}
}
private static class ExPort {
private Export ex;
private int index;
private Name name;
private ExPort(Name name, Export ex, int index) {
this.name = name;
this.ex = ex;
this.index = index;
}
public String toString() { if (name == null) return null; return name.toString(); }
public void print() {
System.out.println(" Name: "+name);
System.out.println(" Ex: "+ex);
System.out.println(" int: "+index);
}
}
// -------------------------------------------------------------------------
private ScanChainElement getScanChainElement(String name, String sin) {
ScanChainElement e = scanChainElements.get(name+"_"+sin);
return e;
}
private PassThroughCell getPassThroughCell(String name, String sin) {
PassThroughCell p = passThroughCells.get(name+"_"+sin);
return p;
}
// -------------------------------------------------------------------------
// Entry point. Starts building a scan chain
// takes care of adding extra hierarchy if start of chain is not at top level.
// other methods are top-down only
// Hierarchy denoted by Stack, start (jtag controller) node is at the top of the stack (pop'd off first)
private SubChainInst startChain(Chain chain, Stack<Nodable> startNode, String startPortName) {
// get top nodable
if (startNode == null) return null;
if (startNode.size() == 0) return null;
Nodable no = startNode.remove(0);
if (startNode.isEmpty()) {
// no is the jtagController, start following the chain
Port startPort = getPort(no, startPortName);
if (startPort == null) {
System.out.println("Can't find specified start port "+startPortName+" on jtag controller "+jtagController.name);
return null;
}
if (DEBUG) System.out.println("Found startNode. Starting chain "+chain.name+" from port "+startPortName);
SubChainInst subChainInst = appendChain(chain, getOtherPorts(startPort));
if (subChainInst == null) {
// no chains at this level, make dummy with output so next level up can follow export up
return new SubChainInst(null, startPort, null, null);
}
return subChainInst;
}
// need to descend to get to start of chain
SubChain subChain = new SubChain(no.getName(), 0);
SubChainInst curInst = new SubChainInst(null, null, no, subChain);
chain.addSubChainInst(curInst);
if (DEBUG) System.out.println("Recursing from "+no.getParent().describe(true)+" into "+no.getName()+", looking for startNode");
// recurse, call will add inst to chain
SubChainInst inst = startChain(subChain, startNode, startPortName);
if( inst == null) return null;
// continue chain, get export at lower level
Port subOutPort = inst.getOutport();
if (subOutPort != null) {
ExPort outEx = getExportedPort(subOutPort);
// find associated port on nodable at this level for export
if (outEx != null) {
Port outport = getPort(no, outEx.name.toString());
curInst.setOutport(outport);
// continue chain at this level
if (DEBUG) System.out.println("Continuing chain in "+no.getParent().describe(true)+" from port "+outport.name);
inst = appendChain(chain, getOtherPorts(outport));
if (inst != null)
curInst = inst;
}
}
return curInst;
}
private SubChainInst getSubChain(Port inport) {
Nodable no = inport.no;
if (DEBUG) System.out.println("getSubChain for NodeInst: "+no.getName()+", sin: "+inport);
NodeProto np = no.getProto();
SubChainInst inst;
// check if this is a pass through element
PassThroughCell p = getPassThroughCell(np.getName(), inport.name.toString());
if (p != null) {
// find output port
Port outport = getPort(no, p.outport);
// make dummy subchain with no length
SubChain sub = new SubChain(p.cellName, -1);
sub.setPassThrough(true);
inst = new SubChainInst(inport, outport, no, sub);
if (DEBUG) System.out.println(" ...matched pass through cell "+p.cellName);
return inst;
}
// check if this is a scan chain element
if (no.isCellInstance()) {
ScanChainElement e = getScanChainElement(np.getName(), inport.name.toString());
if (e != null) {
SubChain sub;
Port outport;
if (no.getNodeInst().getNameKey().isBus()) {
// conglomerate into one subchain
int size = no.getNodeInst().getNameKey().busWidth();
sub = new SubChain(no.getNodeInst().getName(), size, e.access, e.clears,
e.dataport, e.dataport2);
//sub = new SubChain(no.getNodeInst().getName(), size, e.access, e.clears,
// getScanDataNet(no, e.dataport), getScanDataNet(no, e.dataport2));
Nodable lastNo = Netlist.getNodableFor(no.getNodeInst(), size-1);
outport = getPort(lastNo, e.outport);
no = no.getNodeInst();
} else {
sub = new SubChain(no.getName(), 1, e.access, e.clears,
e.dataport, e.dataport2);
//sub = new SubChain(no.getName(), 1, e.access, e.clears,
// getScanDataNet(no, e.dataport), getScanDataNet(no, e.dataport2));
// find output port
outport = getPort(no, e.outport);
}
inst = new SubChainInst(inport, outport, no, sub);
if (DEBUG) System.out.println(" ...matched scan chain element "+e.name);
return inst;
}
// check if this is the jtag controller, which signals the end of the chain
if (jtagCell != null && np.getName().equals(jtagCell.getName())) {
if (DEBUG) System.out.println(" ...matched end of chain, port "+inport);
inst = new SubChainInst(inport, null, no, endChain);
return inst;
}
// otherwise, descend into cell and get subchain (entity) for it
Cell sch = ((Cell)np).contentsView();
if (sch == null) sch = (Cell)np;
if (DEBUG) System.out.println(" ...descending into "+sch);
inst = getSubChain(sch, inport);
return inst;
}
//System.out.println("Error! Scan chain terminated on node "+ni.getName()+" ("+ni.getParent().getName()+")");
return null;
}
// private DataNet getScanDataNet(Nodable no, DataNet definition) {
// if (definition == null) return null;
// String name = getNetName(no, definition.net);
// return new DataNet(name, definition.options);
// }
// get the network name connect to port 'portName' on 'no'. Returns null if none found.
private String getNetName(Nodable no, String portName) {
String netName = null;
PortInst pi = no.getNodeInst().findPortInst(portName);
if (pi == null) return null;
if (pi.hasConnections()) {
// if (pi.getConnections().hasNext()) {
ArcInst ai = pi.getConnections().next().getArc();
// see if there is a bus name
Name busName = no.getParent().getNetlist(SHORT_RESISTORS).getBusName(ai);
if (busName == null) {
netName = no.getParent().getNetlist(SHORT_RESISTORS).getNetwork(ai, 0).getName();
} else {
netName = busName.toString();
}
}
return netName;
}
/**
* Create the subchain for the cell
* @param cell the cell
* @param inport the scan data in port on the nodeinst for this cell
* @return a new Entity that contains the scan chain for this cell
*/
private SubChainInst getSubChain(Cell cell, Port inport) {
// get the export corresponding to the nodeInst input port
Nodable no = inport.no;
Export inputEx = cell.findExport(inport.pp.getNameKey());
ExPort schInPort = new ExPort(inport.name, inputEx, inport.index);
// look up entity
String key = cell.describe(false) + schInPort.name.toString();
Entity ent = entities.get(key);
Port outport = null;
// if not found, create it now
if (ent == null) {
// verify that sinport is part of cell
if (inputEx == null) {
System.out.println("Error! In "+cell+", scan data input Export "+inport.name+" not found.");
return null;
}
ent = new Entity(cell);
ent.setInExPort(schInPort);
entities.put(key, ent);
List<Port> nextPorts = getOtherPorts(schInPort);
SubChainInst lastInst = appendChain(ent, nextPorts);
if (lastInst != null) {
// last inst is instance within cell, find export in cell on same network
Port lastOutport = lastInst.getOutport();
if (lastOutport != null) {
ExPort outEx = getExportedPort(lastOutport);
if (outEx == null) {
System.out.println("Error! In "+cell+", last element \""+lastOutport.no.getName()+"\", output port \""+lastOutport+"\""+
" does not connect to another scan element, is not exported from cell, and does not terminate at the JTAG Controller");
} else {
ent.setOutExPort(outEx);
// get output port on nodeinst corresponding to export
outport = getPort(no, outEx.name.toString());
}
}
}
if (DEBUG) System.out.println("Completed Entity "+ent.name+". inport: "+schInPort+", outport: "+ent.getOutExPort());
} else {
// definition found, just grab output port instance info
ExPort outEx = ent.getOutExPort();
if (outEx != null) {
outport = getPort(no, outEx.name.toString());
}
}
// wrap with instance info
return new SubChainInst(inport, outport, no, ent);
}
/**
* Append any scan chain elements to the chain. Returns the number
* of elements appended
* @param chain
* @param ports
* @return the last instance added to the chain. May be null if nothing added
*/
private SubChainInst appendChain(Chain chain, List<Port> ports) {
ArrayList<Chain> possibleChains = new ArrayList<Chain>();
ArrayList<SubChainInst> chainLastInstances = new ArrayList<SubChainInst>();
// each port may or may not lead to a chain of scan chain elements
for (Port p : ports) {
SubChainInst inst = getSubChain(p);
if (inst == null) continue; // possible dead end
Chain tempChain = new Chain("temp", -1, -1);
SubChain sub = inst.content;
if (sub == endChain) {
tempChain.addSubChainInst(inst);
// store if found good chain
if (tempChain.getSubChainSize() > 0) {
possibleChains.add(tempChain);
chainLastInstances.add(inst);
}
continue; // end of chain
}
if (sub != null) {
if (sub.getLength() > 0 || sub.getSubChainSize() > 0 || sub.isPassThrough()) {
// add to chain if has any content, or if it's a pass through cell
tempChain.addSubChainInst(inst);
}
}
// Note: Here we don't care if sub is null. Only the instance info is
// important from this point on to continue following the chain
// continue along chain
Port outport = inst.getOutport();
if (outport == null) continue;
SubChainInst last = inst;
if (DEBUG) System.out.println("Setting last to "+last);
List<Port> nextPorts = getOtherPorts(outport);
SubChainInst appendLast = appendChain(tempChain, nextPorts);
if (appendLast != null) {
if (DEBUG) System.out.println("Replacing last with "+appendLast);
last = appendLast;
}
// store if found good chain
if (tempChain.getSubChainSize() > 0) {
possibleChains.add(tempChain);
chainLastInstances.add(last);
}
}
// check if any chains found, or if too many found
if (possibleChains.size() == 0) return null;
if (possibleChains.size() > 1) {
System.out.print("Error! Found more than one chain branching from port set: ");
Port p = null;
for (Iterator<Port> it = ports.iterator(); it.hasNext(); ) {
p = it.next();
System.out.print(p.no.getName()+":"+p.name+", ");
}
System.out.println("in cell "+p.no.getParent().describe(false));
}
// append only chain, return last instance in chain
Chain temp = possibleChains.get(0);
for (Iterator<SubChainInst> it = temp.getSubChainInsts(); it.hasNext(); ) {
SubChainInst inst = it.next();
chain.addSubChainInst(inst);
}
return chainLastInstances.get(0);
}
private void postProcessEntitiesRemovePassThroughs() {
for (Entity ent : entities.values()) {
ent.removePassThroughs();
}
}
private void postProcessEntitiesPhase1() {
int reduced = 1;
while (reduced > 0) {
reduced = 0;
/* can't do this anymore with dataNet and dataNet2 attributes
for (Entity ent : entities.values()) {
// if lots of subchains with the same clears and access, consolidate them
if (ent.getSubChainSize() > 1) {
SubChainInst consolidated = null;
List newList = new ArrayList();
// merge subchains with the same access and clears
for (Iterator it2 = ent.getSubChainInsts(); it2.hasNext(); ) {
SubChainInst inst = it2.next();
SubChain sub = inst.getSubChain();
if (sub.access == null || sub.clears == null ||
sub.length <= 0 || sub.getSubChainSize() > 0) {
// can't consolidate
consolidated = null;
newList.add(inst);
} else {
// consolidatable
if (consolidated != null &&
consolidated.getSubChain().access.equals(sub.access) &&
consolidated.getSubChain().clears.equals(sub.clears) && isMergable(inst)) {
// consolidate if they match
consolidated.getSubChain().length += sub.length;
consolidated.setOutport(inst.getOutport());
reduced++;
} else {
consolidated = new SubChainInst(inst.getInport(), inst.getOutport(), inst.no, (SubChain)inst.getSubChain().clone());
newList.add(consolidated);
}
}
}
ent.replaceSubChainInsts(newList);
}
}
*/
for (Entity ent : entities.values()) {
// if only one sub chain that contains no other sub chains, fold into this
if (ent.length <= 0 && ent.getSubChainSize() == 1) {
SubChainInst inst = ent.getSubChainInsts().next();
SubChain sub = inst.getSubChain();
if (sub.length > 0 && sub.getSubChainSize() == 0 && isFlattenable(inst)) {
// fold into parent
ent.length = sub.length;
ent.access = sub.access;
ent.clears = sub.clears;
// on first iteration, do not add sub name, because net is not inside sub
if (sub.dataNet != null)
ent.dataNet = new DataNet("x" + inst.getName() + "." + sub.dataNet.net, sub.dataNet.options);
else
ent.dataNet = null;
if (sub.dataNet2 != null)
ent.dataNet2 = new DataNet("x" + inst.getName() + "." + sub.dataNet2.net, sub.dataNet2.options);
else
ent.dataNet2 = null;
ent.remove(inst);
reduced++;
}
}
// flatten any specified sub chain cells
if (ent.length <= 0) {
// see if any sub cells should be flattened
for (int i=0; i<ent.getSubChainSize(); i++) {
SubChain sub = ent.getSubChain(i);
SubChainInst inst = ent.getSubChainInst(i);
if (sub instanceof Entity) {
Entity subEnt = (Entity)sub;
if ((cellsToFlatten.get(subEnt.cell) != null) &&
isFlattenable(inst) && sub.length < 0 && sub.getSubChainSize() > 0) {
// merge subchain's subchains into this entity
ent.remove(ent.getSubChainInst(i));
ent.addAllSubChainInsts(i, sub.getSubChainsInsts());
reduced++;
}
}
}
}
}
}
}
// private boolean isMergable(SubChainInst inst) {
// if (!inst.getName().matches(".*?@.*")) return false;
// return true;
// }
private boolean isFlattenable(SubChainInst inst) {
if (!inst.getName().matches(".*?@.*")) return false;
return true;
}
// -------------------------------------- Port Manipulation ---------------------------------
private ExPort getExportedPort(Port port) {
if (port == null) return null;
Cell cell = port.no.getParent();
// list of all portinsts on net
Netlist netlist = cell.getNetlist(SHORT_RESISTORS);
Network net = netlist.getNetwork(port.no, port.pp, port.index);
for (Iterator<PortProto> it = cell.getPorts(); it.hasNext(); ) {
Export ex = (Export)it.next();
Name name = ex.getNameKey();
for (int i=0; i<name.busWidth(); i++) {
if (netlist.getNetwork(ex, i) == net)
return new ExPort(name.subname(i), ex, i);
}
}
return null;
}
/**
* Get a list of other Ports on the same network as the ExPort.
* The returned list does not include the Port corresponding to the ExPort
* @param inport the ExPort
* @return a list of Port objects
*/
private ArrayList<Port> getOtherPorts(ExPort inport) {
// convert ExPort to Port
PortInst pi = inport.ex.getOriginalPort();
NodeInst ni = pi.getNodeInst();
Netlist netlist = ni.getParent().getNetlist(SHORT_RESISTORS);
Network net = netlist.getNetwork(inport.ex, inport.index);
Port port = null;
PortProto pp = pi.getPortProto();
if (pp instanceof Export) {
PortProto equiv = ((Export)pp).getEquivalent();
if (equiv != null)
pp = equiv;
}
for (Iterator<Nodable> it = netlist.getNodables(); it.hasNext(); ) {
Nodable no = it.next();
if (no.getNodeInst() == ni) {
if (net == netlist.getNetwork(no, pp, inport.index)) {
port = new Port(inport.name, no, pp, inport.index);
break;
}
}
}
if (port == null) {
port = new Port(inport.name, ni, pp, inport.index);
ArrayList<Port> list = new ArrayList<Port>();
list.add(port);
return list;
//System.out.println("Error: no other ports connected to export "+inport.ex.getName()+" (make sure the export is on an off-page node).");
//return null;
}
return getOtherPorts(port, false);
}
/**
* Get a list of other Ports on the same network as inport. List
* does not include inport.
* @param inport
* @return a list of Port objects
*/
private ArrayList<Port> getOtherPorts(Port inport) {
return getOtherPorts(inport, true);
}
/**
* Get a list of other Ports on the same network as inport. List
* does not include inport.
* @param inport
* @param ignoreInport true to not include inport in the list
* @return a list of Port objects
*/
private ArrayList<Port> getOtherPorts(Port inport, boolean ignoreInport) {
if (inport == null) return null;
ArrayList<Port> ports = new ArrayList<Port>();
Cell cell = inport.no.getParent();
Netlist netlist = cell.getNetlist(SHORT_RESISTORS);
Network net = netlist.getNetwork(inport.no, inport.pp, inport.index);
for (Iterator<Nodable> it = netlist.getNodables(); it.hasNext(); ) {
Nodable no = it.next();
for (Iterator<PortProto> it2 = no.getProto().getPorts(); it2.hasNext(); ) {
PortProto pp = it2.next();
Name name = pp.getNameKey();
for (int i=0; i<name.busWidth(); i++) {
if (ignoreInport) {
if ((no == inport.no) && (pp == inport.pp) && (i == inport.index))
continue;
}
if (netlist.getNetwork(no, pp, i) == net) {
// found matching port
Name subname = name;
if (name.busWidth() > 1) subname = name.subname(i);
Port p = new Port(subname, no, pp, i);
ports.add(p);
}
}
}
}
if (ports.size() == 0) {
if (!ignoreInport) {
ports.add(inport);
return ports;
}
System.out.println("Warning: no other ports connected to port "+inport.name+" on node "+inport.no.getName()+" in cell "+inport.no.getParent().describe(false));
}
return ports;
}
/**
* Get a port from a port name on a Nodable
* @param no the nodable
* @param portName the port name including bus index, such as foo[1][2]
* @return a Port, or null if none found
*/
private Port getPort(Nodable no, String portName) {
for (Iterator<PortProto> it = no.getProto().getPorts(); it.hasNext(); ) {
PortProto pp = it.next();
Name name = pp.getNameKey();
for (int i=0; i<name.busWidth(); i++) {
Name subname = name.subname(i);
if (subname.toString().equals(portName)) {
return new Port(subname, no, pp, i);
}
}
}
System.out.println("Could not find "+portName+" on "+no.getName());
return null;
}
/**
* Get an export in a cell from a exportName. The export
* name may include a bus index, such as foo[1][2]
* @param cell the cell
* @param exportName the export name
* @return an ExPort
*/
private ExPort getExPort(Cell cell, String exportName) {
for (Iterator<PortProto> it = cell.getPorts(); it.hasNext(); ) {
Export ex = (Export)it.next();
Name name = ex.getNameKey();
for (int i=0; i<name.busWidth(); i++) {
Name subname = name.subname(i);
if (subname.toString().equals(exportName)) {
return new ExPort(subname, ex, i);
}
}
}
return null;
}
}