/* -*- 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.simulation.test;
import com.sun.electric.database.hierarchy.*;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.text.Name;
import com.sun.electric.database.network.Netlist;
import com.sun.electric.database.network.Network;
import com.sun.electric.tool.io.output.CellModelPrefs;
import java.util.*;
import java.io.*;
/**
* This class implements
* User: gainsley
* Date: Nov 15, 2005
*/
public class ScanChainXML extends HierarchyEnumerator.Visitor {
private HashMap<Cell,JtagController> jtagControllers;
private JtagController currentJtagController;
private HashMap<Cell,Cell> cellsToFlatten;
private HashMap<Cell,Set<TraceElement>> elements;
private HashMap<String,String> dataNetMap; // key: full hierarchical name, value: flat name
private HashMap<Starter,Starter> starters;
private HashMap<Starter,String> duplicateStarters; // key: chain, value: chain to duplicate
private HashMap<String,String> startFromExport; // key: export name, value: chain name
private List<String> chipNames;
private int currentChipName = 0;
private String outputFile;
private PrintWriter out;
private File outFile;
private boolean debugTracing;
private boolean optimize;
private String chipTDI;
private String chipTDO;
private List<String> scanElementInstanceNames;
private boolean generateScanDataNets;
private HashMap<Cell,Integer> elementsFlatCount; // flat usage count
public ScanChainXML() {
cellsToFlatten = new HashMap<Cell,Cell>();
elements = new HashMap<Cell,Set<TraceElement>>();
dataNetMap = new HashMap<String,String>();
starters = new HashMap<Starter,Starter>();
duplicateStarters = new HashMap<Starter,String>();
startFromExport = new HashMap<String,String>();
jtagControllers = new HashMap<Cell,JtagController>();
chipNames = new ArrayList<String>();
outputFile = null;
out = new PrintWriter(System.out);
outFile = null;
debugTracing = false;
optimize = true;
chipTDI = "TDI";
chipTDO = "TDO";
scanElementInstanceNames = new ArrayList<String>();
generateScanDataNets = true;
elementsFlatCount = new HashMap<Cell,Integer>();
}
/**
* 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;
}
// extract chains from hierarchy
HierarchyEnumerator.enumerateCell(cell, VarContext.globalContext, this, Netlist.ShortResistors.ALL);
// issue warnings for hier instance names specified that were not found
for (String name : scanElementInstanceNames) {
System.out.println("Warning: instance not found in hierarchy: "+name);
}
List<Starter> chains = new ArrayList<Starter>();
// the jtag controllers start from the TDI export
Set<TraceElement> ents = elements.get(cell);
Entity jtagControllerChain = null;
for (TraceElement e : ents) {
if (!(e instanceof Entity)) continue;
if (e.getInport() != null && e.getInport().equals(chipTDI)) {
jtagControllerChain = (Entity)e;
break;
}
}
if (jtagControllerChain == null && startFromExport.keySet().size() == 0) {
System.out.println("Unable to find JtagController chain that starts with export "+chipTDI+" and ends with export "+chipTDO+". Aborting");
return;
}
// get starters at this cell
for (Starter s : starters.keySet()) {
if (s.getCell() == cell) {
chains.add(s);
}
}
// create list of jtag instances to represent chain of jtag controllers
// Each jtag controller is set in a "chip", because the test code can
// handle multiple chips, but not multiple jtag controllers per chip.
List<JtagInstance> jtags = new ArrayList<JtagInstance>();
Set<Entity> entitiesUsed = new TreeSet<Entity>();
if (jtagControllerChain != null) {
List<VarContext> contexts = jtagControllerChain.getContextsOfJtagInstances();
for (VarContext context : contexts) {
// create a new jtag controller for each instance
Nodable no = context.getNodable();
Cell jtagCell = (Cell)no.getProto();
JtagController controller = jtagControllers.get(jtagCell);
if (controller == null) {
controller = jtagControllers.get(jtagCell.contentsView());
}
if (controller == null) {
System.out.println("Unable to find jtag controller for "+context.getInstPath(".")+", cell "+jtagCell.describe(false));
continue;
}
JtagInstance inst = new JtagInstance(context, controller, getNextChipName());
jtags.add(inst);
}
// Find out which starter chains belong to which jtag instances
for (Starter s : chains) {
VarContext context = s.getContextOfFirstElement(VarContext.globalContext, jtagControllers);
if (context == null) {
System.out.println("No context found for starter "+s.getKey());
continue;
}
String hier = context.getInstPath(".");
//System.out.println("Starter "+s.getKey()+" has context "+hier);
boolean used = false;
for (JtagInstance inst : jtags) {
if (context.getInstPath(".").equals(inst.getContext().getInstPath("."))) {
// if there are duplicate chains from this one, add copy
for (Map.Entry<Starter,String> dup : duplicateStarters.entrySet()) {
Starter copy = dup.getKey();
String origChain = dup.getValue();
if (copy.getCell() == inst.controller.getCell() && origChain.equals(s.getChain())) {
copy.copyContentsOf(s);
inst.addStarter(copy);
}
}
inst.addStarter(s);
// Determine which Entity definitions should be written to the file
entitiesUsed.addAll(s.getEntitiesUsed());
used = true;
break;
}
}
if (!used)
System.out.println("Warning: starter "+s.getKey()+" not used by any jtag controllers");
}
}
Collections.sort(chains);
//
// Get list of chains to write when starting from Exports
//
Set<TraceElement> topCellEntities = elements.get(cell);
List<Starter> chainsFromExports = new ArrayList<Starter>();
for (String export : startFromExport.keySet()) {
if (jtagControllerChain == null) {
// create dummy controller chain
jtagControllerChain = new Entity(cell, "TDI", "TDO", false, new ArrayList<Instance>(), null);
}
String chainName = startFromExport.get(export);
TraceElement chain = null;
for (TraceElement e : topCellEntities) {
if (e.getInport() != null && e.getInport().equals(export)) {
chain = e;
break;
}
}
if (chain != null && (chain instanceof Entity)) {
Entity ent = (Entity)chain;
Starter starter = new Starter(ent.getCell(), ent.getOutport(),
ent.getInstances(), chainName, 0, null, null);
chainsFromExports.add(starter);
chains.add(starter);
entitiesUsed.addAll(starter.getEntitiesUsed());
}
}
if (chainsFromExports.size() != 0) {
// create dummy jtag instance
JtagInstance inst = new JtagInstance(VarContext.globalContext, currentJtagController, getNextChipName());
for (Starter s : chainsFromExports) {
inst.addStarter(s);
}
jtags.add(inst);
}
// 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 out flat data net specs as entities
if (generateScanDataNets) {
for (Starter chain : chains) {
if (chain.getLength() == 0) continue;
out.println("<!ENTITY "+chain.getChain()+"_dataNets '");
chain.writeDataNets(out, new StringBuffer(" "), VarContext.globalContext, dataNetMap);
out.println("'>");
}
}
//
// Perform any optimizations
// This needs to occur after writing the dataNets, but before
// writing the entities and chains for the hierarchical scan chain
//
if (optimize) {
optimize(chains);
}
// print entities
for (Entity ent : entitiesUsed) {
ent.writeDefinition(out);
}
out.println("]>");
out.println();
// write chains
StringBuffer indent = new StringBuffer();
out.println("<ChainG>");
indent.append("\t");
out.println(indent+"<system>");
indent.append("\t");
int totalFlatCount = 0;
for (JtagInstance jtag : jtags) {
List<Starter> starters = jtag.getStarters();
if (starters == null || starters.size() == 0) {
System.out.println("No chains defined as starting from jtag instance "+jtag.getHierName());
continue;
}
System.out.println("Writing chains for jtag instance "+jtag.getHierName());
Collections.sort(starters, new StarterByOpcodeCompare());
String chipName;
int lengthIR;
chipName = jtag.getChipName();
lengthIR = jtag.controller.lengthIR;
// write out all chains
out.println(indent+"<chip name=\""+chipName+"\" lengthIR=\""+lengthIR+"\">");
indent.append("\t");
for (Starter chain : starters) {
int length = chain.getLength();
totalFlatCount += length;
System.out.println("Chain "+chain.chain+" has length "+length);
if (length == 0) continue;
chain.write(out, indent);
}
indent.setLength(indent.length() - 1);
out.println(indent+"</chip>");
}
if (generateScanDataNets) {
// write out all data nets
out.println(indent+"<scandatanets>");
indent.append("\t");
for (Starter chain : chains) {
if (chain.getLength() == 0) continue;
out.println(indent+"<datachain name=\""+chain.getChain()+"\"> &"+chain.getChain()+"_dataNets; </datachain>");
}
indent.setLength(indent.length() - 1);
out.println(indent+"</scandatanets>");
}
indent.setLength(indent.length() - 1);
out.println(indent+"</system>");
indent.setLength(indent.length() - 1);
out.println("</ChainG>");
out.flush();
if (outFile != null) {
System.out.println("Wrote XML file to "+outFile.getAbsolutePath());
out.flush();
out.close();
} else {
System.out.println("Wrote XML file to console");
}
System.out.println("Total Flat Count by Summation of Chains: "+totalFlatCount);
System.out.println("Total Flat Count by Hierarchy Enumeration: "+elementsFlatCount.get(cell));
if (elementsFlatCount.get(cell) != totalFlatCount) {
System.out.println("Mismatch, printing Hierarchy Enumerated count by cell:");
printFlatCount(cell, 0, new HashMap<Cell,Cell>());
System.out.println("Please Note that the count may mismatch if the boundary scan chain is counted twice by the XML");
}
}
/**
* 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 libName name of the library containing the cell
* @param cellName name of the cell to be defined as a scan chain element.
* @param access what can be done with the data. A combination of "R" for read,
* "W" for write, and "S" for shadow. For example: "RW".
* @param clears the state set after master clear, "H" for high, "L" for low, "-" for unused.
* @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 libName, String cellName, String access, String clears,
String inport, String outport) {
addScanChainElementAllViews(getCell(libName, cellName), access, clears, inport, outport, false, "", "", null);
}
/**
* 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 libName name of the library containing the cell
* @param cellName name of the cell to be defined as a scan chain element.
* @param access what can be done with the data. A combination of "R" for read,
* "W" for write, and "S" for shadow. For example: "RW".
* @param clears the state set after master clear, "H" for high, "L" for low, "-" for unused.
* @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 libName, String cellName, String access, String clears,
String inport, String outport, String dataport, String dataport2) {
addScanChainElementAllViews(getCell(libName, cellName), access, clears, inport, outport, false, dataport, dataport2, null);
}
/**
* 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 libName name of the library containing the cell
* @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 libName, String cellName, String inport, String outport) {
addScanChainElementAllViews(getCell(libName, cellName), null, null, inport, outport, true, "", "", null);
}
/**
* 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.
* This version limits the pass through to a particular instance specified
* by the instance name field.
* @param libName name of the library containing the cell
* @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]"
* @param instanceName limit to the particular instance name.
*/
public void addPassThroughCell(String libName, String cellName, String inport, String outport, String instanceName) {
addScanChainElementAllViews(getCell(libName, cellName), null, null, inport, outport, true, "", "", instanceName);
if (instanceName != null)
scanElementInstanceNames.add(instanceName);
}
/**
* 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) {
Cell cell = getCell(libName, cellName);
if (cell != null) {
cellsToFlatten.put(cell, cell);
}
cell = getCellOtherView(cell);
if (cell != null) {
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.
* This method is ignored if "startFromExport" is used.
* @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) {
setJtagController(jtagLib, jtagCellName, lengthIR, "TDI", "TDOb");
}
/**
* Specify the JTAG Controller. All scan chains are assumed to start, and end, at the
* JTAG Controller. This specifies the jtag controller.
* This method is ignored if "startFromExport" is used.
* @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, String inport, String outport) {
Cell cell = getCell(jtagLib, jtagCellName);
currentJtagController = new JtagController(cell, inport, outport, false, lengthIR);
addJtagControllerAllViews(cell, inport, outport, lengthIR);
}
/**
* Set the port name for the chip TDI signal. This is the input data to the
* first jtag controller on chip.
* @param TDIport
*/
public void setChipTDI(String TDIport) {
chipTDI = TDIport;
}
/**
* Set the port name for the chip TDO signal. This is the output data from the
* last jtag controller on the chip.
* @param TDOport
*/
public void setChipTDO(String TDOport) {
chipTDO = TDOport;
}
/**
* 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.
* This method is ignored if "startFromExport" is used.
* @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 sinPortName the port name that scan data returns to.
* May contain index info, such as "leaf1[8]"
* @param chainName the name given to this scan chain
*/
public void addJtagPort(int opcode, String soutPortName, String sinPortName, String chainName) {
if (currentJtagController == null) {
System.out.println("Can't add port "+soutPortName+" because the jtag controller has not been defined yet");
return;
}
//currentJtagController.addPort(soutPortName, chainName, opcode);
addJtagPortStarterAllViews(currentJtagController.getCell(), sinPortName, soutPortName, chainName, opcode);
/* Starter s = new Starter(currentJtagController.getCell(), soutPortName, null, chainName, opcode, null);
// special case: for boundary scan chains we specify two chains
// from the same port. The algorithm assumes unique chains, so
// we have to duplicate the chain when printing it out, not during enumeration
for (Starter olds : starters.keySet()) {
if (olds.getCell() == currentJtagController.getCell() &&
olds.getOutport().equals(soutPortName)) {
// already specified
duplicateStarters.put(s, olds.getChain());
return;
}
}
starters.put(s, chainName);
add(s);
// also add as an end element
ScanChainElement e = new ScanChainElement(currentJtagController.getCell(), "", "", sinPortName, null, true, "", "");
add(e);*/
}
/**
* 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 the export that starts the chain
* @param chainName the name for the chain
*/
public void startFromExport(String exportName, String chainName) {
startFromExport.put(exportName, chainName);
}
/**
* Specify the name of the chip. Used when writing the chip name to the file.
* @param name the chip name
*/
public void setChipName(String name) {
chipNames.add(name);
}
/**
* Set true to generate scan data nets in the output file, false not to.
* Then is set to true by default.
* @param generate whether or not to generate the scan data nets in the output file.
*/
public void generateScanDataNets(boolean generate) {
generateScanDataNets = generate;
}
private String getNextChipName() {
String name;
if (currentChipName >= chipNames.size()) {
name = "dummy"+currentChipName;
System.out.println("Warning: creating dummy chip name because not enough calls to setChipName() vs number of jtag controllers");
} else {
name = chipNames.get(currentChipName);
}
currentChipName++;
return 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");
outFile = null;
outputFile = null;
}
}
/**
* Call this method to print out tracing debug information
*/
public void debugTracing() {
debugTracing = true;
}
private void addScanChainElementAllViews(Cell cell, String access, String clears,
String inport, String outport, boolean passThrough,
String dataport, String dataport2,
String instanceName) {
if (cell == null) return;
for (Iterator<Cell> it = cell.getCellGroup().getCells(); it.hasNext(); ) {
Cell otherCell = it.next();
if (otherCell.isSchematic() || otherCell.getView() == View.LAYOUT) {
ScanChainElement e = new ScanChainElement(otherCell, access, clears, inport, outport,
passThrough, dataport, dataport2, instanceName);
add(e);
}
}
}
private void addJtagPortStarterAllViews(Cell cell, String sinPortName, String soutPortName, String chainName, int opcode) {
if (cell == null) return;
for (Iterator<Cell> it = cell.getCellGroup().getCells(); it.hasNext(); ) {
Cell otherCell = it.next();
if (otherCell.isSchematic() || otherCell.getView() == View.LAYOUT) {
Starter s = new Starter(otherCell, soutPortName, null, chainName, opcode, null, null);
// special case: for boundary scan chains we specify two chains
// from the same port. The algorithm assumes unique chains, so
// we have to duplicate the chain when printing it out, not during enumeration
boolean add = true;
for (Starter olds : starters.keySet()) {
if (olds.getCell() == otherCell &&
olds.getOutport().equals(soutPortName)) {
// already specified
duplicateStarters.put(s, olds.getChain());
add = false;
}
}
if (add) {
starters.put(s, s);
add(s);
// ender
ScanChainElement e = new ScanChainElement(otherCell, "", "", sinPortName, null, true, "", "", null);
add(e);
}
}
}
}
private void addJtagControllerAllViews(Cell cell, String inport, String outport, int lengthIR) {
if (cell == null) return;
for (Iterator<Cell> it = cell.getCellGroup().getCells(); it.hasNext(); ) {
Cell otherCell = it.next();
if (otherCell.isSchematic() || otherCell.getView() == View.LAYOUT) {
JtagController jtag = new JtagController(otherCell, inport, outport, false, lengthIR);
jtagControllers.put(otherCell, jtag);
add(jtag);
}
}
}
private Cell getCellOtherView(Cell cell) {
View otherView = null;
if (cell.getView() == View.LAYOUT) otherView = View.SCHEMATIC;
if (cell.isSchematic()) otherView = View.LAYOUT;
if (cell.isIcon()) otherView = View.LAYOUT;
Cell otherCell = getCell(cell, otherView);
return otherCell;
}
private Cell getCell(String libName, String cellName) {
Library lib = Library.findLibrary(libName);
if (lib == null) {
System.out.println("Warning: Did not find library "+libName+" for cell "+cellName);
return null;
}
Cell cell = lib.findNodeProto(cellName);
if (cell == null) {
System.out.println("Warning: Did not find cell "+cellName+", in library "+libName);
return null;
}
if (cell.isIcon()) {
cell = getCell(cell, View.SCHEMATIC);
if (cell == null) {
System.out.println("Warning: Did not find cell schematic cell for "+cellName+"{ic}, in library "+libName);
return null;
}
}
return cell;
}
private Cell getCell(Cell cell, View view) {
if (view == null) return null;
for (Iterator<Cell> it = cell.getCellGroup().getCells(); it.hasNext(); ) {
Cell acell = it.next();
if (acell.getView() == view) {
return acell;
}
}
return null;
}
private void add(TraceElement e) {
Set<TraceElement> traceElements = elements.get(e.cell);
if (traceElements == null) {
traceElements = new TreeSet<TraceElement>();
elements.put(e.getCell(), traceElements);
}
for (TraceElement ele : traceElements) {
// same definition
if (ele.getClass() != e.getClass()) continue;
if (ele.getCell() != e.getCell()) continue;
boolean namesSame = false;
if (ele.getInstanceName() != null && e.getInstanceName() != null) {
if (ele.getInstanceName().equals(e.getInstanceName()))
namesSame = true;
}
if (ele.getInstanceName() == null && e.getInstanceName() == null)
namesSame = true;
if (!namesSame) continue;
boolean inportsSame = false;
if (ele.inport == e.inport) inportsSame = true;
if (ele.inport != null && e.inport != null && ele.inport.equals(e.inport)) inportsSame = true;
if (!inportsSame) continue;
boolean outportsSame = false;
if (ele.outport == e.outport) inportsSame = true;
if (ele.outport != null && e.outport != null && ele.outport.equals(e.outport)) outportsSame = true;
if (inportsSame && outportsSame) {
System.out.println("Warning, already cell "+e.cell.describe(false)+", inport "+e.inport+" already added");
return;
}
}
traceElements.add(e);
}
// ------------------------------------------------------------------
public boolean enterCell(HierarchyEnumerator.CellInfo info) {
Cell cell = info.getCell();
Set<TraceElement> traceElements = elements.get(cell);
if (traceElements == null) return true;
boolean scanChainElement = false;
for (TraceElement e : traceElements) {
if (e instanceof ScanChainElement) {
// We are inside a ScanChainElement
// get the data net, and do not enumerate
ScanChainElement sc = (ScanChainElement)e;
mapDataNet(info, sc.getDataNet());
mapDataNet(info, sc.getDataNet2());
scanChainElement = true;
}
}
if (scanChainElement) return false;
return true;
}
private void mapDataNet(HierarchyEnumerator.CellInfo info, DataNet dataNet) {
if (dataNet == null) return;
Network net = getNetwork(info.getNetlist(), dataNet.getName());
String flatContext = "";
String netName = dataNet.getName();
if (net != null) {
// may go up hier
HierarchyEnumerator.NetDescription desc = info.netIdToNetDescription(info.getNetID(net));
netName = desc.getNet().getName();
flatContext = desc.getCellInfo().getContext().getInstPath(".x");
}
if (net == null) {
// see if name is hierarchical (go down hier)
VarContext netContext = info.getContext();
String name = dataNet.getName();
Cell cell = info.getCell();
if (name.contains(".")) {
String hier[] = name.split("\\.");
// go down hierarchy
for (int i=0; i<hier.length-1; i++) {
String instName = hier[i];
for (Iterator<Nodable> nit = cell.getNodables(); nit.hasNext(); ) {
Nodable no = nit.next();
if (no.getName().equals(instName) && no.isCellInstance()) {
Cell proto = (Cell)no.getProto();
cell = proto.getEquivalent();
if (cell == null) cell = proto;
netContext = netContext.push(no);
}
}
}
// last field is net name, check for it in cell
net = getNetwork(cell.getNetlist(), hier[hier.length-1]);
flatContext = netContext.getInstPath(".x");
netName = hier[hier.length-1];
}
}
if (net == null) {
System.out.println("Error: Cannot find data net "+dataNet.getName()+" in cell "+info.getCell()+", please check dataNet spec for this cell");
return;
}
String contextualName = ScanChainElement.getDataNetPath(dataNet, info.getContext());
if (flatContext != null && !flatContext.equals("")) {
flatContext = "x"+flatContext+".";
} else {
flatContext = "";
}
netName = netName.replace('.', '_');
String flatName = flatContext + netName;
dataNetMap.put(contextualName, flatName);
//if (debugTracing) System.out.println(" mapping dataNet "+contextualName+" --> "+flatName);
}
public void exitCell(HierarchyEnumerator.CellInfo info) {
Set<TraceElement> processedElements = elements.get(info.getCell());
if (processedElements != null) {
boolean foundinst = false;
for (TraceElement e : processedElements) {
if (e.getInstanceName() != null) {
foundinst = true; break;
}
}
if (!foundinst) return; // already cached, no instances named
}
// see if we have already built an entity for this cell
//if (!doNotSkip && elements.get(info.getCell()) != null) return; // already cached
/*
if (info.getContext() == VarContext.globalContext) {
System.out.println("global context marker for breakpoint");
}
*/
String currentHier = info.getContext().getInstPath(".");
if (debugTracing) System.out.println("---------------------------");
if (debugTracing) System.out.println("Tracing cell "+info.getCell().describe(false)+" "+currentHier);
// check for scan chain elements
HashMap<String,Instance> instancesByInput = new HashMap<String,Instance>();
HashMap<String,Instance> instancesByOutput = new HashMap<String,Instance>();
List<Instance> instancesWithNoInput = new ArrayList<Instance>();
boolean instanceElementPresent = false;
int flatCount = 0;
for (Iterator<NodeInst> it = info.getCell().getNodes(); it.hasNext(); ) {
NodeInst ni = it.next();
if (!ni.isCellInstance()) continue; // not a cell, continue
if (ni.isIconOfParent()) continue;
Cell cell = (Cell)ni.getProto();
Cell contents = cell.contentsView();
if (contents != null) cell = contents; // get contents view
if (info.getCell().isSchematic() && enumerateLayoutView(cell)) {
Cell laycell = getLayoutView(cell);
if (laycell != null) cell = laycell;
}
Set<TraceElement> traceElements = elements.get(cell);
if (traceElements == null) continue;
// Each cell could have several elements defined for it,
// since an element is defined by a Cell + input port pair
for (TraceElement e : traceElements) {
// Arrays here are handled for cells and pass throughs, but
// special cased for scan chain element so that we have one entry with
// a length > 1
boolean arrayHere = true;
if (e instanceof ScanChainElement) {
ScanChainElement sc = (ScanChainElement)e;
if (!sc.isPassThrough()) {
arrayHere = false;
flatCount += ni.getNameKey().busWidth();
}
}
if (ni.getNameKey().busWidth() > 1 && arrayHere) {
// expand arrayed nodeinsts that are not scan chain elements
for (int i=0; i<ni.getNameKey().busWidth(); i++) {
Nodable no = Netlist.getNodableFor(ni, i);
createInstance(no, info, instancesByInput, instancesByOutput,
instancesWithNoInput, e);
}
} else {
// just create instance for the nodeinst
// but check if instanceName is set. If so, must match, otherwise, ignore
String instanceName = info.getContext().push(ni).getInstPath(".");
if (e.getInstanceName() != null) {
if (e.getInstanceName().equals(instanceName)) {
createInstance(ni, info, instancesByInput, instancesByOutput,
instancesWithNoInput, e);
scanElementInstanceNames.remove(instanceName);
}
instanceElementPresent = true;
} else {
// no instance name means all instances should be recognized
createInstance(ni, info, instancesByInput, instancesByOutput,
instancesWithNoInput, e);
}
}
}
Integer ii = elementsFlatCount.get(cell);
if (ii != null) {
flatCount += (ii * ni.getNameKey().busWidth());
}
}
elementsFlatCount.put(info.getCell(), new Integer(flatCount));
// instances with no input are starters.
// also, instances with no prev are local starters (their input is likely exported)
for (Instance inst : instancesByInput.values()) {
if (inst.prev == null) {
instancesWithNoInput.add(inst);
}
}
// get traceElements so we can put newly created Entities in them
Set<TraceElement> traceElements = elements.get(info.getCell());
if (traceElements == null) {
traceElements = new TreeSet<TraceElement>();
elements.put(info.getCell(), traceElements);
}
// create Entity. It may be a starter if start of chain is in this cell
String entName = null;
if (instanceElementPresent) {
entName = info.getContext().getInstPath(".");
}
int startercount = 0;
// find the start instances
for (Instance inst : instancesWithNoInput) {
// this is a start instance, follow it to the end, and make an Entity out of it
Instance last = inst;
List<Instance> instances = new ArrayList<Instance>();
instances.add(last);
while (last.next != null) {
last = last.next;
instances.add(last);
}
// check if input of start is exported, last check is to make sure it's a real export
// and not a power/ground export
String inex = null;
if (inst.innet != null && inst.innet.isExported() && inst.innet.getExports().hasNext()) {
inex = inst.innet.getName();
}
// check if output of end is exported
String outex = null;
if (last.outnet != null && last.outnet.isExported() && last.outnet.getExports().hasNext()) {
outex = last.outnet.getName();
}
Entity ent;
String entity = "Entity";
// check if first instance is the start of a chain
if (starters.containsKey(inst.e) && (inst.e instanceof Starter)) {
// this entity will be the new starter
// starters are always flat
entName = info.getContext().getInstPath(".");
Starter oldStart = (Starter)inst.e;
ent = new Starter(info.getCell(), outex, instances, oldStart.chain, oldStart.opcode, oldStart, entName);
//System.out.println("Creating Starter for cell "+info.getCell().describe(false)+
// ", in="+inex+", out="+outex+", chain="+oldStart.chain);
starters.put((Starter)ent, (Starter)ent);
startercount++;
//starters.remove(inst.e);
entity = "Starter";
if (outex == null && last.e.outport != null) {
// not an ender (ends have null outport definitions), and not exported, must be error
String outnet = last.outnet == null ? null : last.outnet.getName();
if (inst.e.isPassThrough()) {
System.out.println("Warning: pass-through instance "+last.no.getName()+" in cell "+info.getCell().describe(false)+
" has nothing connected to its output port "+last.e.outport+"("+outnet+")");
} else {
System.out.println("Error: instance "+last.no.getName()+" in cell "+info.getCell().describe(false)+
" has nothing connected to its output port "+last.e.outport+"("+outnet+")");
}
continue;
}
} else {
// create Entity
// check if this chain is just a passthrough chain, and has no real elements
boolean passThrough = true;
for (Instance entinst : instances) {
if (!entinst.getTraceElement().isPassThrough()) {
passThrough = false;
break;
}
}
// if totally unconnected and this chain contains only passthrough elements, don't issue an error
if (inex == null && inst.getTraceElement().getInport() != null &&
outex == null && last.getTraceElement().getOutport() != null) {
if (passThrough) {
String innet = inst.innet == null ? null : inst.innet.getName();
String outnet = last.outnet == null ? null : last.outnet.getName();
System.out.println("Warning: Pass through chain of "+info.getCell().describe(false)+" from input net \""+
innet+"\" to output net \""+outnet+"\" has both networks not exported, so I am ignoring it");
continue;
}
}
if (inex == null && inst.e.inport != null) {
// not a starter, and start network is not exported. Must be error
String innet = inst.innet == null ? null : inst.innet.getName();
if (passThrough) {
System.out.println("Warning: pass-through instance "+inst.no.getName()+" in cell "+info.getCell().describe(false)+
" has nothing connected to its input port "+inst.e.inport+"("+innet+")");
} else {
System.out.println("Error: instance "+inst.no.getName()+" in cell "+info.getCell().describe(false)+
" has nothing connected to its input port "+inst.e.inport+"("+innet+")");
}
continue;
}
if (outex == null && last.e.outport != null) {
// not an ender (ends have null outport definitions), and not exported, must be error
String outnet = last.outnet == null ? null : last.outnet.getName();
if (passThrough) {
System.out.println("Warning: pass-through instance "+last.no.getName()+" in cell "+info.getCell().describe(false)+
" has nothing connected to its output port "+last.e.outport+"("+outnet+")");
} else {
System.out.println("Error: instance "+last.no.getName()+" in cell "+info.getCell().describe(false)+
" has nothing connected to its output port "+last.e.outport+"("+outnet+")");
}
continue;
}
ent = new Entity(info.getCell(), inex, outex, passThrough, instances, entName);
}
if (debugTracing) System.out.println("Creating "+entity+" for cell "+info.getCell().describe(false)+
", in="+inex+", out="+outex+", key="+ent.getKey());
traceElements.add(ent);
}
/*
if (startercount > 0) {
if (entName != null)
System.out.println("Created "+startercount+" starters in "+entName);
else
System.out.println("Created "+startercount+" starters in "+info.getCell().describe(false));
}
*/
}
private void createInstance(Nodable ni, HierarchyEnumerator.CellInfo info,
HashMap<String,Instance> instancesByInput,
HashMap<String,Instance> instancesByOutput,
List<Instance> instancesWithNoInput, TraceElement e) {
Instance inst = new Instance(ni, info.getContext(), ni.getNameKey().busWidth(), e);
// Hook up Instances in linked list, which denotes order of chain.
Nodable inno = ni;
Nodable outno = ni;
if ((ni instanceof NodeInst) && ni.getNameKey().busWidth() > 1) {
NodeInst nin = (NodeInst)ni;
inno = Netlist.getNodableFor(nin, 0);
outno = Netlist.getNodableFor(nin, nin.getNameKey().busWidth()-1);
}
if (e.inport != null) {
// hook up to predecessor instance
Network innet = getNetwork(inno, info.getNetlist(), e.inport);
Instance prev = null;
if (innet != null) {
prev = instancesByOutput.get(innet.getName());
if (prev != null) {
// make sure one inst not driving two insts
//checkBadFanout(prev, prev.next, inst);
inst.prev = prev;
prev.next = inst;
if (debugTracing) System.out.println(" Chaining "+prev.describeOutnet()+" -> "+inst.describeInnet());
}
// check for merging of chains
Instance branch = instancesByInput.get(innet.getName());
if (branch != null) {
if (prev != null) {
System.out.println("Error! Chain branches from: "+prev.describeOutput());
} else {
System.out.println("Error! Chain branches from net "+innet.getName()+" in cell "+ni.getParent().describe(false));
}
System.out.println(" into: "+inst.describeInput()+" (key="+inst.getTraceElement().getKey()+")");
System.out.println(" into: "+branch.describeInput()+" (key="+branch.getTraceElement().getKey()+")");
}
inst.innet = innet;
instancesByInput.put(innet.getName(), inst);
} else {
System.out.println("Error: Can't find input network for port "+e.getInport()+" on element "+e.getCell().describe(false));
}
} else {
instancesWithNoInput.add(inst);
}
if (e.outport != null) {
// hook up to successor instance
Network outnet = getNetwork(outno, info.getNetlist(), e.outport);
Instance next = null;
if (outnet != null) {
next = instancesByInput.get(outnet.getName());
if (next != null) {
//checkBadFanin(inst, next.prev, next);
inst.next = next;
next.prev = inst;
if (debugTracing) System.out.println(" Chaining "+inst.describeOutnet()+" -> "+next.describeInnet());
}
Instance merge = instancesByOutput.get(outnet.getName());
if (merge != null) {
if (next != null) {
System.out.println("Error! Chain merges into "+next.describeInput());
} else {
System.out.println("Error! Chain merges into net "+outnet.getName()+" in cell "+ni.getParent().describe(false));
}
System.out.println(" from: "+inst.describeOutput()+" (key="+inst.getTraceElement().getKey()+")");
System.out.println(" from: "+merge.describeOutput()+" (key="+merge.getTraceElement().getKey()+")");
}
inst.outnet = outnet;
instancesByOutput.put(outnet.getName(), inst);
} else {
System.out.println("Error: Can't find output network for port "+e.getOutport()+" on element "+e.getCell().describe(false));
}
}
if (debugTracing) {
String innet = inst.innet == null ? null : inst.innet.getName();
String outnet = inst.outnet == null ? null : inst.outnet.getName();
System.out.println(" Created instance "+inst.no.getName()+
" in="+e.inport+"("+innet+")"+
" out="+e.outport+"("+outnet+") for element "+e.getKey());
}
}
public boolean visitNodeInst(Nodable ni, HierarchyEnumerator.CellInfo info) {
// We need to push into ScanChainElements one level to
// get at the data net. We also need to push into everything else,
// except primitives which cannot be pushed into
if (ni.isCellInstance()) {
Cell cell = (Cell)ni.getProto();
Cell schcell = cell.contentsView();
if (schcell == null) schcell = cell;
if (cell.isSchematic() && enumerateLayoutView(schcell)) {
Cell layCell = getLayoutView(schcell);
if (layCell != null) {
HierarchyEnumerator.enumerateCell(layCell, info.getContext().push(ni), this);
return false;
}
}
}
return true;
}
private boolean enumerateLayoutView(Cell cell) {
return CellModelPrefs.spiceModelPrefs.isUseLayoutView(cell);
}
private Cell getLayoutView(Cell cell) {
Cell layCell = null;
for (Iterator<Cell> it = cell.getCellGroup().getCells(); it.hasNext(); ) {
Cell c = it.next();
if (c.getView() == View.LAYOUT) {
return c;
}
}
return null;
}
private void checkBadFanout(Instance prev, Instance next1, Instance next2) {
if (next1 == null || next2 == null) return;
if (prev == null) return;
// all non-null, issue error
System.out.println("Error! Chain branches from: "+prev.describeOutput());
System.out.println(" into: "+next1.describeInput());
System.out.println(" into: "+next2.describeInput());
}
private void checkBadFanin(Instance prev1, Instance prev2, Instance next) {
if (prev1 == null || prev2 == null) return;
if (next == null) return;
// all non-null, issue error
System.out.println("Error! Chain merges into "+next.describeInput());
System.out.println(" from: "+prev1.describeOutput());
System.out.println(" from: "+prev2.describeOutput());
}
private HashMap<Nodable,String> jtagControllerNames = new HashMap<Nodable,String>();
private void setJtagControllerName(Nodable no, String name) {
jtagControllerNames.put(no, name);
}
private String getJtagControllerName(Nodable no) {
return jtagControllerNames.get(no);
}
private void printFlatCount(Cell cell, int indent, Map<Cell,Cell> alreadyPrinted) {
if (elementsFlatCount.get(cell) == null) return;
if (elementsFlatCount.get(cell) == 0) return;
if (alreadyPrinted.containsKey(cell)) return;
alreadyPrinted.put(cell, cell);
StringBuffer buf = new StringBuffer();
for (int i=0; i<indent; i++) {
buf.append(" ");
}
System.out.println(buf+cell.describe(false)+": "+elementsFlatCount.get(cell));
for (Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); ) {
NodeInst ni = it.next();
if (!ni.isCellInstance()) continue; // not a cell, continue
if (ni.isIconOfParent()) continue;
Cell c = (Cell)ni.getProto();
Cell contents = c.contentsView();
if (contents != null) c = contents; // get contents view
if (cell.isSchematic() && enumerateLayoutView(c)) {
Cell laycell = getLayoutView(c);
if (laycell != null) c = laycell;
}
printFlatCount(c, indent+2, alreadyPrinted);
}
}
// ------------------------------------------------------------------
private static class TraceElement implements Comparable {
private final Cell cell;
private final String inport;
private final String outport;
private final boolean passThrough;
private String hierInstanceName; // if limited to a particular instance
private TraceElement(Cell cell, String inport, String outport,
boolean passThrough, String hierInstanceName) {
this.cell = cell;
this.inport = inport;
this.outport = outport;
this.passThrough = passThrough;
this.hierInstanceName = hierInstanceName;
}
public static String getKey(Cell cell, String inport, String hierInstanceName) {
String name = (hierInstanceName == null) ? "" : "_"+hierInstanceName;
String key = cell.getLibrary().getName()+"_"+cell.getName()+"_"+inport+name;
key = key.replaceAll("[\\[\\]@]", "_");
return key;
}
public String getKey() {
return getKey(cell, inport, hierInstanceName);
}
public String getInstanceName() { return hierInstanceName; }
public void setInstanceName(String name) { hierInstanceName = name; }
public Cell getCell() { return cell; }
public int compareTo(Object o) {
TraceElement e = (TraceElement)o;
return getKey().compareTo(e.getKey());
}
public boolean equals(Object o) {
TraceElement e = (TraceElement)o;
return getKey().equals(e.getKey());
}
public String getOutport() { return outport; }
public String getInport() { return inport; }
public boolean isPassThrough() { return passThrough; }
}
/** Defines a Scan Chain Element, which contains one bit of scan storage */
private static class ScanChainElement extends TraceElement {
private final String access;
private final String clears;
private final DataNet dataport;
private final DataNet dataport2;
private ScanChainElement(Cell cell, String access, String clears, String inport, String outport,
boolean passThrough, String dataport, String dataport2, String hierInstanceName) {
super(cell, inport, outport, passThrough, hierInstanceName);
this.access = access;
this.clears = clears;
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);
}
public DataNet getDataNet() { return dataport; }
public DataNet getDataNet2() { return dataport2; }
public static String getDataNetPath(DataNet dataNet, VarContext context) {
if (dataNet == null) return null;
return context.getInstPath(".")+"."+dataNet.getName();
}
public String getAccess() { return access; }
public String getClears() { return clears; }
public boolean combinable(ScanChainElement other) {
if (access == null && other.getAccess() != null) return false;
if (access != null && other.getAccess() == null) return false;
if (access != null && other.getAccess() != null && !access.equals(other.getAccess())) return false;
if (clears == null && other.getClears() != null) return false;
if (clears != null && other.getClears() == null) return false;
if (clears != null && other.getClears() != null && !clears.equals(other.getClears())) return false;
return true;
}
}
/**
* The networks that the scan chain elements reads from or
* writes to.
*/
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; }
public String getName() { return net; }
public String getOptions() { return options; }
}
/**
* Defines the scan chain elements for a Cell + Input Port pair.
*/
private static class Entity extends TraceElement {
private List<Instance> instances;
private Entity(Cell cell, String inport, String outport, boolean passThrough,
List<Instance> instances, String hierInstanceName) {
super(cell, inport, outport, passThrough, hierInstanceName);
this.instances = new ArrayList<Instance>();
if (instances != null) this.instances.addAll(instances);
}
public void writeDefinition(PrintWriter out) {
if (getLength() == 0) return;
out.println("<!ENTITY "+getKey()+" '");
List<String> usedNames = new ArrayList<String>();
for (Instance inst : instances) {
inst.write(out, new StringBuffer("\t"), usedNames);
}
out.println("'>");
}
public void writeContents(PrintWriter out, StringBuffer indent) {
List<String> usedNames = new ArrayList<String>();
for (Instance inst : instances) {
inst.write(out, indent, usedNames);
}
}
public void writeDataNets(PrintWriter out, StringBuffer indent, VarContext context, HashMap<String,String> dataNetMap) {
for (Instance inst : instances) {
inst.writeDataNets(out, indent, context, dataNetMap);
}
}
public int getLength() {
int len = 0;
for (Instance inst : instances) {
len += inst.getLength();
}
return len;
}
public int flattenInstances(HashMap<Cell,Cell> cellsToFlatten, HashMap<Entity,Cell> alreadyFlattened) {
int flattened = 0;
if (alreadyFlattened.containsKey(this)) return 0;
if (this instanceof Starter) return 0; // can't flatten starters?
for (int i=0; i<instances.size(); i++) {
Instance inst = instances.get(i);
// flatten instance if it's entity contains only 1 thing
TraceElement ele = inst.getTraceElement();
if (ele instanceof Entity) {
Entity ent = (Entity)ele;
// make sure the sub element is complete optimized in terms of flattening
flattened += ent.flattenInstances(cellsToFlatten, alreadyFlattened);
if (ent.getRealInstances().size() == 1 ||
cellsToFlatten.containsKey(ent.getCell())) {
// flatten, bring contents of instance into this entity
instances.remove(i);
List<Instance> newInstances = new ArrayList<Instance>();
for (Instance subinst : ent.getRealInstances()) {
Instance newinst = new Instance(subinst.getNodable(),
subinst.getContext(), subinst.getLength(), subinst.getTraceElement());
if (ent.getRealInstances().size() == 1) {
// keep upper level name
newinst.setNameOverride(inst.getName());
} else {
// use combination of instances to prevent name conflicts
newinst.setNameOverride(inst.getName()+"."+subinst.getName());
}
newInstances.add(newinst);
}
instances.addAll(i, newInstances);
flattened++;
break; // have to break and start over because we modified list
}
}
}
alreadyFlattened.put(this, getCell());
return flattened;
}
// returns the scan chain element if it is combinable, otherwise returns null
private int combineInstances() {
List<ArrayedNameList> arrayedNames = new ArrayList<ArrayedNameList>();
int combosDone = 0;
int maxDimension = 0;
// build list of arrayed names. Cannot combine unless it is a scanChainElement
for (Instance inst : instances) {
if (inst.getTraceElement() instanceof ScanChainElement) {
ArrayedNameList name = new ArrayedNameList(inst.getName());
arrayedNames.add(name);
int m = name.numDimensions();
if (m > maxDimension) maxDimension = m;
} else {
arrayedNames.add(null);
}
}
// start from lowest dimension
for (int m=0; m<maxDimension; m++) {
// check if any names are combinable, and if their instances are combinable
ArrayedNameList last = null;
for (int i=0; i<arrayedNames.size(); i++) {
ArrayedNameList next = arrayedNames.get(i);
if (last == null || next == null) {
last = next;
continue;
}
// see if combinable both by name, and by scan chain element type
Instance lastinst = instances.get(i-1);
Instance nextinst = instances.get(i);
ScanChainElement lastE = (ScanChainElement)lastinst.getTraceElement();
ScanChainElement nextE = (ScanChainElement)nextinst.getTraceElement();
ArrayedNameList combined = last.combineSequentialNumeric(next, m);
if (lastE.combinable(nextE) && combined != null) {
// can combine them
Instance newInst = new Instance(lastinst.getNodable(), lastinst.getContext(),
lastinst.getLength()+nextinst.getLength(), lastinst.getTraceElement());
newInst.setNameOverride(combined.toString());
instances.remove(i-1); // remove last
instances.remove(i-1); // remove next
arrayedNames.remove(i-1); // keep name indices aligned with instances
arrayedNames.remove(i-1);
instances.add(i-1, newInst); // replace both by newInst
arrayedNames.add(i-1, combined);
next = combined;
i--; // adjust index
combosDone++;
}
last = next;
}
}
return combosDone;
}
protected List<Instance> getInstances() { return instances; }
protected List<Instance> getRealInstances() {
List<Instance> realinstances = new ArrayList<Instance>();
for (Instance inst : instances) {
if (inst.getTraceElement() instanceof ScanChainElement) {
ScanChainElement ele = (ScanChainElement)inst.getTraceElement();
if (ele.isPassThrough()) continue;
}
realinstances.add(inst);
}
return realinstances;
}
protected List<Instance> getFlatInstances() {
List<Instance> flatinstances = new ArrayList<Instance>();
for (Instance inst : instances) {
TraceElement e = inst.e;
if (e.isPassThrough()) continue;
if (e instanceof Entity) {
Entity ent = (Entity)e;
flatinstances.addAll(ent.getFlatInstances());
}
else {
flatinstances.add(inst);
}
}
return flatinstances;
}
public List<VarContext> getContextsOfJtagInstances() {
List<VarContext> contexts = new ArrayList<VarContext>();
getContextsOfJtagInstances(contexts, VarContext.globalContext);
return contexts;
}
private void getContextsOfJtagInstances(List<VarContext> contexts, VarContext parentContext) {
for (Instance inst : instances) {
if (inst.e instanceof Entity) {
Entity ent = (Entity)inst.e;
ent.getContextsOfJtagInstances(contexts, parentContext.push(inst.getNodable()));
continue;
}
if (inst.e instanceof JtagController) {
contexts.add(parentContext.push(inst.getNodable()));
}
}
}
// may return null if no valid elements in instances (this ignores passthroughs
public VarContext getContextOfFirstElement(VarContext parentContext, HashMap<Cell,JtagController> jtagControllers) {
for (Instance inst : instances) {
if (inst.e.isPassThrough()) continue;
if (inst.e instanceof Entity) {
Entity ent = (Entity)inst.e;
if (jtagControllers.get(ent.getCell()) != null) {
// this is a jtag controller instance
return parentContext.push(inst.getNodable());
}
VarContext context = ent.getContextOfFirstElement(parentContext.push(inst.getNodable()), jtagControllers);
if (context == null) continue;
return context;
}
if (inst.e instanceof ScanChainElement || inst.e instanceof JtagController) {
VarContext context = parentContext.push(inst.getNodable());
return context;
}
}
return null;
}
public void copyContentsOf(Entity ent) {
instances.clear();
instances.addAll(ent.getInstances());
}
public Set<Entity> getEntitiesUsed() {
Set<Entity> entities = new TreeSet<Entity>();
for (Instance inst : instances) {
TraceElement ele = inst.getTraceElement();
if (ele instanceof Entity) {
Entity ent = (Entity)ele;
entities.add(ent);
entities.addAll(ent.getEntitiesUsed());
}
}
return entities;
}
public String toString() { return getKey(); }
}
private static class Starter extends Entity {
private final String chain;
private final int opcode;
private String chipName;
private Starter child;
private Starter(Cell cell, String outport, List<Instance> instances, String chain, int opcode,
Starter child, String hierInstanceName) {
super(cell, null, outport, false, instances, hierInstanceName);
this.chain = chain;
this.opcode = opcode;
this.chipName = "Temp_";
this.child = child;
}
public void write(PrintWriter out, StringBuffer indent) {
out.println(indent+"<chain name=\""+chain+"\" opcode=\""+Integer.toBinaryString(opcode)+"\">");
indent.append("\t");
writeContents(out, indent);
indent.setLength(indent.length() - 1);
out.println(indent+"</chain>");
}
public void writeData(PrintWriter out, StringBuffer indent, VarContext context, HashMap<String,String> dataNetMap) {
out.println(indent+"<chain name=\""+chain+"\" opcode=\""+Integer.toBinaryString(opcode)+"\">");
indent.append("\t");
writeDataNets(out, indent, context, dataNetMap);
indent.setLength(indent.length() - 1);
out.println(indent+"</chain>");
}
public String getKey() {
return getKey(getCell(), getOrigStarterInstanceName()+"_"+chain, getInstanceName());
}
public void setChipName(String chipName) {
this.chipName = chipName;
if (child != null) child.setChipName(chipName);
}
public String getChipName() {
return chipName;
}
public String getChain() {
String chipName = getChipName();
return chipName+"_"+chain;
//return getOrigStarterInstanceName()+"_"+chain;
}
public String getOrigStarterInstanceName() {
if (child == null) return getInstanceName();
String name = child.getOrigStarterInstanceName();
if (name == null) name = getInstanceName();
return name;
}
}
public static class StarterByOpcodeCompare implements Comparator<Starter> {
public int compare(Starter s1, Starter s2) {
int compareChipName = s1.getChipName().compareTo(s2.getChipName());
if (compareChipName == 0) {
if (s1.opcode == s2.opcode) return 0;
if (s1.opcode < s2.opcode) return -1;
return 1;
}
return compareChipName;
}
}
/**
* An instance of a Trace Element: either a real scan chain element,
* or an Entity, which is just a cell contain scan chain elements.
*/
private static class Instance {
private final Nodable no;
private final VarContext context;
private final int length;
private final TraceElement e;
private Instance prev; // for building linked list of instances in order
private Instance next;
private Network innet;
private Network outnet;
private String nameOverride;
private Instance(Nodable no, VarContext context, int length, TraceElement e) {
this.no = no;
this.context = context;
this.length = length;
this.e = e;
this.prev = null;
this.next = null;
this.innet = null;
this.outnet = null;
this.nameOverride = null;
}
public String describeOutput() {
return no.getParent().getName()+"."+no.getName()+"."+e.outport;
}
public String describeInput() {
return no.getParent().getName()+"."+no.getName()+"."+e.inport;
}
public String describeOutnet() {
return no.getName()+"["+e.outport+"("+outnet+")]";
}
public String describeInnet() {
return no.getName()+"["+e.inport+"("+innet+")]";
}
public void write(PrintWriter out, StringBuffer indent, List<String> usedNames) {
String name = getName();
if (usedNames.contains(name) && e.inport != null)
name = name +"_" + e.inport;
if (e instanceof Entity) {
// just reference entity
Entity ent = (Entity)e;
if (ent.getLength() == 0) return;
out.println(indent+"<subchain name=\""+name+"\"> &"+e.getKey()+"; </subchain>");
usedNames.add(name);
} else {
// scan chain element
ScanChainElement ele = (ScanChainElement)e;
if (ele.isPassThrough()) return;
out.print(indent+"<subchain name=\""+name+"\"");
out.print(" length=\""+length+"\"");
if (ele.access != null) out.print(" access=\""+ele.access+"\"");
if (ele.clears != null) out.print(" clears=\""+ele.clears+"\"");
out.println(" />");
}
}
public void writeDataNets(PrintWriter out, StringBuffer indent, VarContext context, HashMap<String,String> dataNetMap) {
if (e instanceof Entity) {
// just reference entity
Entity ent = (Entity)e;
ent.writeDataNets(out, indent, context.push(no), dataNetMap);
} else {
// scan chain element
ScanChainElement ele = (ScanChainElement)e;
if (ele.isPassThrough()) return;
for (int i=0; i<no.getNameKey().busWidth(); i++) {
Nodable thisno = no;
if (no instanceof NodeInst) {
thisno = Netlist.getNodableFor((NodeInst)no, i);
}
VarContext subContext = context.push(thisno);
out.print(indent+"<datanet name=\""+subContext.getInstPath(".")+"\"");
String flatName = getFlatDataNet(ele.getDataNet(), subContext, dataNetMap);
String flatName2 = getFlatDataNet(ele.getDataNet2(), subContext, dataNetMap);
if (flatName != null) out.print(" net=\""+flatName+"\"");
if (flatName2 != null) out.print(" net2=\""+flatName2+"\"");
out.println(" />");
}
}
}
private String getFlatDataNet(DataNet dataNet, VarContext context, HashMap<String,String> dataNetMap) {
if (dataNet == null) return null; // null data net, no error
String contextName = ScanChainElement.getDataNetPath(dataNet, context);
if (contextName == null) return null;
String flatName = dataNetMap.get(contextName);
if (flatName == null) {
//System.out.println("Can't find mapping for dataNet "+contextName);
//flatName = "*"+contextName;
return null;
}
return flatName+dataNet.getOptions();
}
public int getLength() {
if (e instanceof ScanChainElement) {
ScanChainElement ele = (ScanChainElement)e;
if (ele.isPassThrough()) return 0;
return length;
}
else {
// just reference entity
Entity ent = (Entity)e;
return ent.getLength();
}
}
public void setNameOverride(String name) { this.nameOverride = name; }
public String getName() { return ( (nameOverride == null) ? no.getName() : nameOverride); }
protected TraceElement getTraceElement() { return e; }
protected Nodable getNodable() { return no; }
protected VarContext getContext() { return context; }
}
/** Defines the Jtag controller from which the scan chains start and end */
private static class JtagController extends TraceElement {
public final int lengthIR;
private JtagController(Cell cell, String inport, String outport, boolean passThrough, int lengthIR) {
super(cell, inport, outport, passThrough, null);
this.lengthIR = lengthIR;
}
public int getLengthIR() { return lengthIR; }
}
private static class JtagInstance {
private final JtagController controller;
private String chipName;
private VarContext context;
private List<Starter> starters;
private JtagInstance(VarContext context, JtagController controller, String chipName) {
this.context = context;
this.controller = controller;
this.chipName = chipName;
this.starters = new ArrayList<Starter>();
}
public String getHierName() { return context.getInstPath("."); }
public String getChipName() { return chipName; }
public VarContext getContext() { return context; }
public void addStarter(Starter s) {
starters.add(s);
s.setChipName(chipName);
}
public List<Starter> getStarters() { return starters; }
}
private static class Range {
private int start;
private int end;
public Range(int start, int end) {
this.start = start;
this.end = end;
}
public int getStart() { return start; }
public int getEnd() { return end; }
public int getRange() { return Math.abs(end-start); }
/**
* Return true if the order is ascending (start < end),
* or false otherwise.
* @param includeSingular if range is 0 (start == end),
* then this method will return the state of this argument
*/
public boolean isAscending(boolean includeSingular) {
if (start < end) return true;
if (start == end) return includeSingular;
return false;
}
/**
* Return true if the order is descending (end < start),
* or false otherwise.
* @param includeSingular if range is 0 (start == end),
* then this method will return the state of this argument
*/
public boolean isDescending(boolean includeSingular) {
if (start > end) return true;
if (start == end) return includeSingular;
return false;
}
public boolean equals(Object o) {
Range r = (Range)o;
if (getStart() == r.getStart() && getEnd() == r.getEnd()) return true;
return false;
}
public String toString() {
if (start == end) return String.valueOf(start);
return (start + ":" + end);
}
/**
* Combines the two ranges if possible, otherwise returns null.
* Ranges must be non-overlapping to be combinable.
* @param next the next range
* @return the combined range, or null if not possible
*/
public Range combine(Range next) {
// start and end must differ by 1
int diff = Math.abs(getEnd()-next.getStart());
if (diff != 1) return null;
// order (ascending/descending) must be the same
if (isAscending(true) && next.isAscending(true)) {
return new Range(getStart(), next.getEnd());
}
if (isDescending(true) && next.isDescending(true)) {
return new Range(getStart(), next.getEnd());
}
return null;
}
}
private static class ArrayIndex {
private List<Object> indices;
boolean numericOnly;
/**
* indices is a string representing some set of indices, of the format:
* <ul>
* <li>indices := block{,block}*
* <li>block := letter | range
* <li>range := digit | digit : digit
* </ul>
* @param indices
*/
public ArrayIndex(String indices) {
this.indices = new ArrayList<Object>();
this.numericOnly = true;
// split by commas
String [] parts = indices.split(",");
for (int i=0; i<parts.length; i++)
addBlock(parts[i]);
}
public ArrayIndex(List<Object> indices) {
numericOnly = true;
this.indices = indices;
for (int i=0; i<indices.size(); i++) {
if (!(indices.get(i) instanceof Range))
numericOnly = false;
}
}
private void addBlock(String block) {
block = block.trim();
if (block.matches("\\d+")) {
// a number
int start = Integer.parseInt(block);
indices.add(new Range(start, start));
} else if (block.matches("\\d+:\\d+")) {
// a range
String [] range = block.split(":");
int start = Integer.parseInt(range[0]);
int end = Integer.parseInt(range[1]);
indices.add(new Range(start, end));
} else if (block.matches("\\w+")) {
indices.add(block);
} else {
System.out.println("Invalid array index "+block);
}
}
public boolean isNumericOnly() { return numericOnly; }
public boolean equals(Object o) {
ArrayIndex a = (ArrayIndex)o;
if (getNumIndices() != a.getNumIndices()) return false;
for (int i=0; i<indices.size(); i++) {
Object index = a.indices.get(i);
if (!indices.get(i).equals(index)) // either string, or a Range object
return false;
}
return true;
}
public int getNumIndices() { return indices.size(); }
public Object getIndex(int index) {
if (index <0 || index > (indices.size()-1)) return null;
return indices.get(index);
}
public String toString() {
StringBuffer str = new StringBuffer();
for (Iterator it = indices.iterator(); it.hasNext(); ) {
Object next = it.next();
if (next instanceof String)
str.append((String)next);
if (next instanceof Range)
str.append(((Range)next).toString());
if (it.hasNext())
str.append(",");
}
return str.toString();
}
/**
* Tries to combine the two array indices if they are sequential
* numeric indices
* @param next
* @return
*/
public ArrayIndex combineSequentialNumeric(ArrayIndex next) {
if (!isNumericOnly()) return null;
if (!next.isNumericOnly()) return null;
Object thisLastObj = getIndex(getNumIndices()-1);
Object otherFirstObj = next.getIndex(0);
if (!(thisLastObj instanceof Range) || !(otherFirstObj instanceof Range)) return null;
Range thisLast = (Range)thisLastObj;
Range otherFirst = (Range)otherFirstObj;
// both should be ranges
if (thisLast == null || otherFirst == null) return null;
Range newRange = thisLast.combine(otherFirst);
if (newRange == null) return null;
List<Object> indices = new ArrayList<Object>(this.indices);
indices.remove(getNumIndices()-1);
indices.add(newRange);
return new ArrayIndex(indices);
}
}
private static class ArrayedName {
private String name;
private List<ArrayIndex> indices; // order of most sig to least sig, unrolling traverses least sig first
public ArrayedName(String arrayedName) {
// break name by brackets
indices = new ArrayList<ArrayIndex>();
String [] parts = arrayedName.trim().split("[\\[\\]]");
for (int i=0; i<parts.length; i++) {
if (i == 0) {
// this is the name
if (parts[0].equals("")) System.out.println("Invalid name for "+arrayedName);
name = parts[0];
continue;
}
String str = parts[i].trim();
if (str.equals("")) continue;
ArrayIndex index = new ArrayIndex(parts[i]);
indices.add(index);
}
// reverse order so that unrolling starts from first indices
Collections.reverse(indices);
}
public ArrayedName(String name, List<ArrayIndex> indices) {
this.name = name;
this.indices = indices;
}
public String getName() { return name; }
public int numDimensions() { return indices.size(); }
public ArrayIndex getIndex(int i) {
if (i < 0 || i > (indices.size()-1)) {
return null;
}
return indices.get(i);
}
/**
* Tries to combine this name with another name if they are in numeric sequence.
* This means the ArrayIndex at the specified degree must be completely numeric.
* If successful, returns the new combined name. Otherwise, returns null.
* @param dimension which dimension of a multi-dimensional array to use, starting from 0
* at the least signficant: name[1:4][1:3], 1:3 is dimension 0, 1:4 is dimension 1
* @return the new combined name, or null if none possible
*/
public ArrayedName combineSequentialNumeric(ArrayedName next, int dimension) {
// check dimension in question, make sure it exists
ArrayIndex index = getIndex(dimension);
if (index == null) return null;
if (!index.isNumericOnly()) return null;
// make sure names are the same
if (!name.equals(next.getName())) return null;
// make sure same dimensions
if (numDimensions() != next.numDimensions()) return null;
// make sure all dimensions besides the one in question are the same
for (int i=0; i<numDimensions(); i++) {
if (i == dimension) continue;
ArrayIndex curi = getIndex(i);
ArrayIndex nexti = next.getIndex(i);
if (!curi.equals(nexti)) {
return null;
}
}
// see if dimension in question is sequential between the two
ArrayIndex nextIndex = next.getIndex(dimension);
ArrayIndex combined = index.combineSequentialNumeric(nextIndex);
if (combined == null) return null;
// sequential, create a new ArrayedName
ArrayList<ArrayIndex> newIndices = new ArrayList<ArrayIndex>();
for (int i=0; i<numDimensions(); i++) {
if (i == dimension) {
newIndices.add(combined);
continue;
}
newIndices.add(getIndex(i));
}
return new ArrayedName(getName(), newIndices);
}
public String toString() {
StringBuffer str = new StringBuffer(name);
// have to go backwards
for (int i=indices.size()-1; i>=0; i--) {
ArrayIndex ind = getIndex(i);
str.append("[");
str.append(ind.toString());
str.append("]");
}
return str.toString();
}
/** Unit Test */
public static void main(String [] args) {
ArrayedName n1 = new ArrayedName("foo[1]");
ArrayedName n2 = new ArrayedName("foo[2]");
printTest(n1, n2, 0);
printTest(n1, n2, 1);
n1 = new ArrayedName("foo[1:2][5:4][1:2]");
n2 = new ArrayedName("foo[1:2][3:1][1:2]");
printTest(n1, n2, 1);
printTest(n1, n2, 2);
}
public static void printTest(ArrayedName n1, ArrayedName n2, int dim) {
System.out.println("Combining dimension "+dim+" of: "+n1+" and "+ n2+" --> "+n1.combineSequentialNumeric(n2, dim));
}
}
// Parses a string of the form: ArrayedName,ArrayedName,....ArrayedName.
private static class ArrayedNameList {
private List<ArrayedName> arrayedNames;
public ArrayedNameList(String name) {
arrayedNames = new ArrayList<ArrayedName>();
List<String> names = new ArrayList<String>();
int openBrackets = 0;
int last = 0;
for (int i=0; i<name.length(); i++) {
char c = name.charAt(i);
if (c == '[') openBrackets++;
if (c == ']') openBrackets--;
if (c == ',' && openBrackets == 0) {
int start = last;
if (start >= 0 && i > start) {
names.add(name.substring(start, i));
}
last = i+1; // skip ,
}
}
if (openBrackets == 0 && last < name.length()) {
names.add(name.substring(last, name.length()));
}
for (String s : names) {
arrayedNames.add(new ArrayedName(s));
}
}
private ArrayedNameList(List<ArrayedName> arrayedNames) {
this.arrayedNames = arrayedNames;
}
public int getNumNames() { return arrayedNames.size(); }
public ArrayedName get(int index) {
if (index < 0 || index > (getNumNames()-1)) return null;
return arrayedNames.get(index);
}
public int numDimensions() {
int maxDimension = 0;
for (int i=0; i<getNumNames(); i++) {
int m = get(i).numDimensions();
if (m > maxDimension) maxDimension = m;
}
return maxDimension;
}
public ArrayedNameList combineSequentialNumeric(ArrayedNameList next, int dimension) {
ArrayedName thisLast = get(getNumNames()-1);
ArrayedName nextFirst = next.get(0);
if (thisLast != null && nextFirst != null) {
// try to combine them
ArrayedName combined = thisLast.combineSequentialNumeric(nextFirst, dimension);
if (combined != null) {
// build new list with combined name
List<ArrayedName> newList = new ArrayList<ArrayedName>();
for (int i=0; i<getNumNames()-1; i++)
newList.add(get(i)); // skip last
newList.add(combined); // add combined
for (int i=1; i<next.getNumNames(); i++) {
newList.add(next.get(i)); // skip first
}
return new ArrayedNameList(newList);
}
}
return null;
}
public String toString() {
StringBuffer buf = new StringBuffer();
for (int i=0; i<getNumNames(); i++) {
ArrayedName name = get(i);
buf.append(name.toString());
if (i < (getNumNames()-1)) // more to come
buf.append(',');
}
return buf.toString();
}
/** Unit Test */
public static void main(String [] args) {
ArrayedNameList n1 = new ArrayedNameList("foo[1],foo[2]");
ArrayedNameList n2 = new ArrayedNameList("foo[3],foo[4]");
printTest(n1, n2, 0);
printTest(n1, n2, 1);
n1 = new ArrayedNameList("foo[1,a],bar[2:4]");
n2 = new ArrayedNameList("bar[5],xxx[2]");
printTest(n1, n2, 0);
printTest(n1, n2, 1);
n1 = new ArrayedNameList("foo[1,a],bar[3:5][2:4]");
n2 = new ArrayedNameList("bar[6][2:4],xxx[2]");
printTest(n1, n2, 0);
printTest(n1, n2, 1);
}
public static void printTest(ArrayedNameList n1, ArrayedNameList n2, int dim) {
System.out.println("Combining dimension "+dim+" of: "+n1+" and "+ n2+" --> "+n1.combineSequentialNumeric(n2, dim));
}
}
// ==========================================================================
/**
* Optimize the specification of the entities and scan chain
*/
private void optimize(List<Starter> chains) {
int done = 1;
//while (done != 0) {
done = 0;
done += flatten(chains);
done += combineInstances();
//}
}
/**
* Flatten out specified entities, and flatten out entities that
* only contain one subchain. This does one pass through the entities,
* several passes are required to optimize everything.
*/
private int flatten(List<Starter> chains) {
int done = 0;
HashMap<Entity,Cell> alreadyFlattened = new HashMap<Entity,Cell>();
for (Starter chain : chains) {
done += chain.flattenInstances(cellsToFlatten, alreadyFlattened);
}
return done;
}
private int combineInstances() {
int done = 0;
for (Set<TraceElement> list : elements.values()) {
for (TraceElement e : list) {
if (e instanceof Entity) {
Entity ent = (Entity)e;
// check if we can flatten any instances
done += ent.combineInstances();
}
}
}
return done;
}
// ==========================================================================
/**
* 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 ExPort getExportedPort(Port port) {
// if (port == null) return null;
//
// Cell cell = port.no.getParent();
//
// // list of all portinsts on net
// Netlist netlist = cell.getNetlist(true);
// 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 the network attached to port 'portName' on nodable 'no'.
* @param no
* @param portName
* @return
*/
private static Network getNetwork(Nodable no, Netlist netlist, 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 netlist.getNetwork(no, pp, i);
}
}
}
System.out.println("Error: Could not find port "+portName+" on "+no.getProto().describe(false)+"("+no.getName()+") in cell "+no.getParent().describe(false));
return null;
}
private static Network getNetwork(Netlist netlist, String name) {
for (Iterator<Network> it = netlist.getNetworks(); it.hasNext(); ) {
Network net = it.next();
if (net.hasName(name)) return net;
}
return null;
}
/**
* 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 static Port getPort(Nodable no, String portName) {
if (portName == null) return null;
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 static 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;
}
}