/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: Generate.java * Generate VHDL from a circuit * Written by Steven M. Rubin, Sun Microsystems. * * 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.Cell; import com.sun.electric.database.hierarchy.Export; import com.sun.electric.database.hierarchy.Nodable; import com.sun.electric.database.network.Global; 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.PortCharacteristic; import com.sun.electric.database.prototype.PortProto; import com.sun.electric.database.text.PrefPackage; import com.sun.electric.database.text.TextUtils; import com.sun.electric.database.text.Version; import com.sun.electric.database.topology.ArcInst; import com.sun.electric.database.topology.Connection; import com.sun.electric.database.topology.NodeInst; import com.sun.electric.database.variable.VarContext; import com.sun.electric.database.variable.Variable; import com.sun.electric.technology.ArcProto; import com.sun.electric.technology.PrimitiveNode; import com.sun.electric.technology.PrimitivePort; import com.sun.electric.technology.technologies.Schematics; import com.sun.electric.tool.simulation.Simulation; import com.sun.electric.tool.user.CompileVHDL; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.prefs.Preferences; /** * This is the VHDL generation facility. */ public class GenerateVHDL extends Topology { /** special codes during VHDL generation */ /** ordinary block */ private static final int BLOCKNORMAL = 0; /** a MOS transistor */ private static final int BLOCKMOSTRAN = 1; /** a buffer */ private static final int BLOCKBUFFER = 2; /** an and, or, xor */ private static final int BLOCKPOSLOGIC = 3; /** an inverter */ private static final int BLOCKINVERTER = 4; /** a nand */ private static final int BLOCKNAND = 5; /** a nor */ private static final int BLOCKNOR = 6; /** an xnor */ private static final int BLOCKXNOR = 7; /** a settable D flip-flop */ private static final int BLOCKFLOPDS = 8; /** a resettable D flip-flop */ private static final int BLOCKFLOPDR = 9; /** a settable T flip-flop */ private static final int BLOCKFLOPTS = 10; /** a resettable T flip-flop */ private static final int BLOCKFLOPTR = 11; /** a general flip-flop */ private static final int BLOCKFLOP = 12; private static final String NORMALCONTINUATIONSTRING = " "; private static final String COMMENTCONTINUATIONSTRING = "-- "; private final VHDLPreferences vp; public static class VHDLPreferences extends PrefPackage { private static final String KEY_VHDL = "SchematicVHDLStringFor"; public Map<PrimitiveNode,String> vhdlNames = new HashMap<PrimitiveNode,String>(); public VHDLPreferences(boolean factory) { super(factory); Preferences techPrefs = getPrefRoot().node(TECH_NODE); Schematics schTech = Schematics.tech(); for (Iterator<PrimitiveNode> it = schTech.getNodes(); it.hasNext(); ) { PrimitiveNode pn = it.next(); String key = KEY_VHDL + pn.getName(); String factoryVhdl = schTech.getFactoryVHDLNames(pn); String vhdl = techPrefs.get(key, factoryVhdl); vhdlNames.put(pn, vhdl); } } /** * Store annotated option fields of the subclass into the speciefied Preferences subtree. * @param prefRoot the root of the Preferences subtree. * @param removeDefaults remove from the Preferences subtree options which have factory default value. */ @Override public void putPrefs(Preferences prefRoot, boolean removeDefaults) { super.putPrefs(prefRoot, removeDefaults); Preferences techPrefs = prefRoot.node(TECH_NODE); Schematics schTech = Schematics.tech(); for (Map.Entry<PrimitiveNode,String> e: vhdlNames.entrySet()) { PrimitiveNode pn = e.getKey(); String key = KEY_VHDL + pn.getName(); String factoryVhdl = schTech.getFactoryVHDLNames(pn); String vhdl = e.getValue(); if (removeDefaults && vhdl.equals(factoryVhdl)) techPrefs.remove(key); else techPrefs.put(key, vhdl); } } } private GenerateVHDL(VHDLPreferences vp) { this.vp = vp; } /** * Method to convert a cell to a list of strings with VHDL in them. * @param cell the Cell to convert. * @return a list of strings with VHDL in them (null on error). */ public static List<String> convertCell(Cell cell, VHDLPreferences vp) { // cannot make VHDL for cell with no ports if (cell.getNumPorts() == 0) { System.out.println("Cannot convert " + cell.describe(false) + " to VHDL: it has no ports"); return null; } GenerateVHDL out = new GenerateVHDL(vp); out.openStringsOutputStream(); out.setOutputWidth(80, false); out.setContinuationString(NORMALCONTINUATIONSTRING); // generate the VHDL if (out.writeCell(cell, null)) return null; // return the array of strings with VHDL return out.closeStringsOutputStream(); } /** * Method to start the output. * Writes the header. */ protected void start() { setContinuationString(COMMENTCONTINUATIONSTRING); writeWidthLimited("-- VHDL automatically generated by the Electric VLSI Design System, version " + Version.getVersion() + "\n"); setContinuationString(NORMALCONTINUATIONSTRING); } protected void done() {} /** * Method to write one level of hierarchy. */ protected void writeCellTopology(Cell cell, String cellName, CellNetInfo cni, VarContext context, Topology.MyCellInfo info) { // write the header writeWidthLimited("\n"); setContinuationString(COMMENTCONTINUATIONSTRING); writeWidthLimited("-------------------- Cell " + cell.describe(false) + " --------------------\n"); setContinuationString(NORMALCONTINUATIONSTRING); Netlist nl = cni.getNetList(); // write the entity section String properCellName = getSafeCellName(cell.getName()); writeWidthLimited("entity " + addString(properCellName, null) + " is port(" + addPortList(cni) + ");\n"); writeWidthLimited(" end " + addString(properCellName, null) + ";\n"); // write the "architecture" line writeWidthLimited("\n"); writeWidthLimited("architecture " + addString(properCellName, null) + "_BODY of " + addString(properCellName, null) + " is\n"); // find all negated arcs int instNum = 1; Map<ArcInst,Integer> negatedHeads = new HashMap<ArcInst,Integer>(); Map<ArcInst,Integer> negatedTails = new HashMap<ArcInst,Integer>(); for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); ) { ArcInst ai = it.next(); if (ai.getHead().isNegated()) negatedHeads.put(ai, new Integer(instNum++)); if (ai.getTail().isNegated()) negatedTails.put(ai, new Integer(instNum++)); } // write prototypes for each node; gather information for NAND, NOR, and XNOR Set<Integer> multiInputNAND = new HashSet<Integer>(); Set<Integer> multiInputNOR = new HashSet<Integer>(); Set<Integer> multiInputNXOR = new HashSet<Integer>(); boolean gotInverters = false; Set<String> cellNamesWritten = new HashSet<String>(); for(Iterator<Nodable> it = nl.getNodables(); it.hasNext(); ) { Nodable no = it.next(); AnalyzePrimitive ap = new AnalyzePrimitive(no, negatedHeads, negatedTails, vp); String pt = ap.getPrimName(); if (pt == null) continue; int special = ap.getSpecial(); // write only once per prototype if (special == BLOCKINVERTER) { gotInverters = true; continue; } if (special == BLOCKNAND) { multiInputNAND.add(new Integer(TextUtils.atoi(pt.substring(4)))); continue; } if (special == BLOCKNOR) { multiInputNOR.add(new Integer(TextUtils.atoi(pt.substring(3)))); continue; } if (special == BLOCKXNOR) { multiInputNXOR.add(new Integer(TextUtils.atoi(pt.substring(4)))); continue; } // ignore component with no ports if (no.getProto().getNumPorts() == 0) continue; pt = getSafeCellName(pt); if (cellNamesWritten.contains(pt)) continue; cellNamesWritten.add(pt); if (no.isCellInstance()) { String parameterizedName = parameterizedName(no, context); CellNetInfo subCni = getCellNetInfo(parameterizedName); if (subCni == null) { System.out.println("ERROR: no subcell information for: " + parameterizedName); continue; } writeWidthLimited(" component " + addString(pt, null) + " port(" + addPortList(subCni) + ");\n"); } else { writeWidthLimited(" component " + addString(pt, null) + " port(" + addPortListPrim(no, special) + ");\n"); } writeWidthLimited(" end component;\n"); } // write prototype for multi-input NAND, NOR, and XNOR for(Integer i : multiInputNAND) { String compName = "nand" + i; cellNamesWritten.add(compName); writeWidthLimited(" component " + compName + " port("); for(int j=1; j<=i.intValue(); j++) { if (j > 1) writeWidthLimited(", "); writeWidthLimited("a" + j); } writeWidthLimited(": in BIT; y: out BIT);\n"); writeWidthLimited(" end component;\n"); } for(Integer i : multiInputNOR) { String compName = "nor" + i; cellNamesWritten.add(compName); writeWidthLimited(" component " + compName + " port("); for(int j=1; j<=i.intValue(); j++) { if (j > 1) writeWidthLimited(", "); writeWidthLimited("a" + j); } writeWidthLimited(": in BIT; y: out BIT);\n"); writeWidthLimited(" end component;\n"); } for(Integer i : multiInputNXOR) { String compName = "xnor" + i; cellNamesWritten.add(compName); writeWidthLimited(" component " + compName + " port("); for(int j=1; j<=i.intValue(); j++) { if (j > 1) writeWidthLimited(", "); writeWidthLimited("a" + j); } writeWidthLimited(": in BIT; y: out BIT);\n"); writeWidthLimited(" end component;\n"); } // write inverter prototype if applicable if (negatedHeads.size() > 0 || negatedTails.size() > 0) gotInverters = true; if (gotInverters) { cellNamesWritten.add("inverter"); writeWidthLimited(" component inverter port(a: in BIT; y: out BIT);\n"); writeWidthLimited(" end component;\n"); } // write internal signals that were used SignalNameLine snl = new SignalNameLine(); for(Iterator<CellSignal> it = cni.getCellSignals(); it.hasNext(); ) { CellSignal cs = it.next(); if (cs.getExport() != null) continue; if (!cs.getNetwork().getArcs().hasNext()) continue; String sigName = addString(cs.getName(), cell); snl.addSignalName(sigName); } for(ArcInst ai : negatedHeads.keySet()) { Integer index = negatedHeads.get(ai); String sigName = "PINV" + index.intValue(); snl.addSignalName(sigName); } for(ArcInst ai : negatedTails.keySet()) { Integer index = negatedTails.get(ai); String sigName = "PINV" + index.intValue(); snl.addSignalName(sigName); } snl.finish(); // write the instances writeWidthLimited("\n"); writeWidthLimited("begin\n"); for(Iterator<Nodable> it = nl.getNodables(); it.hasNext(); ) { Nodable no = it.next(); // ignore component with no ports if (no.getProto().getNumPorts() == 0) continue; int special = BLOCKNORMAL; String pt = no.getProto().getName(); if (!no.isCellInstance()) { AnalyzePrimitive ap = new AnalyzePrimitive(no, negatedHeads, negatedTails, vp); pt = ap.getPrimName(); if (pt == null) continue; special = ap.getSpecial(); } String instname = getSafeCellName(no.getName()); writeWidthLimited(" " + addString(instname, null)); // make sure the instance name doesn't conflict with a prototype name if (cellNamesWritten.contains(instname)) writeWidthLimited("NV"); if (no.isCellInstance()) { String parameterizedName = parameterizedName(no, context); CellNetInfo subCni = getCellNetInfo(parameterizedName); if (subCni == null) { System.out.println("STILL NO SUBCELL INFORMATION FOR "+parameterizedName); continue; } writeWidthLimited(": " + addString(getSafeCellName(pt), null) + " port map(" + addRealPortsCell(subCni, no, negatedHeads, negatedTails, nl) + ");\n"); } else { writeWidthLimited(": " + addString(getSafeCellName(pt), null) + " port map(" + addRealPortsPrim(no, special, negatedHeads, negatedTails, nl) + ");\n"); } } // write pseudo-nodes for all negated arcs for(ArcInst ai : negatedHeads.keySet()) { Integer index = negatedHeads.get(ai); writeWidthLimited(" PSEUDO_INVERT" + index.intValue() + ": inverter port map("); Network net = nl.getNetwork(ai, 0); if (ai.getHeadPortInst().getPortProto().getBasePort().getCharacteristic() == PortCharacteristic.OUT) { writeWidthLimited("PINV" + index.intValue() + ", " + addString(net.describe(false), cell)); } else { writeWidthLimited(addString(net.describe(false), cell) + ", PINV" + index.intValue()); } writeWidthLimited(");\n"); } for(ArcInst ai : negatedTails.keySet()) { Integer index = negatedTails.get(ai); writeWidthLimited(" PSEUDO_INVERT" + index.intValue() + ": inverter port map("); Network net = nl.getNetwork(ai, 0); if (ai.getTailPortInst().getPortProto().getBasePort().getCharacteristic() == PortCharacteristic.OUT) { writeWidthLimited("PINV" + index.intValue() + ", " + addString(net.describe(false), cell)); } else { writeWidthLimited(addString(net.describe(false), cell) + ", PINV" + index.intValue()); } writeWidthLimited(");\n"); } // finish the cell writeWidthLimited("end " + addString(properCellName, null) + "_BODY;\n"); } /****************************** METHODS TO WRITE LIST OF CELL PARAMETERS ******************************/ /** * Method to write actual signals that connect to a cell instance. * @param cni signal information for the cell being instantiated. * @param no the instance node. * @param negatedHeads map of arcs with negated head ends. * @param negatedTails map of arcs with negated tail ends. * @param nl the Netlist for the Cell containing the instance. * @return a string with the connection signals. */ private String addRealPortsCell(CellNetInfo cni, Nodable no, Map<ArcInst,Integer> negatedHeads, Map<ArcInst,Integer> negatedTails, Netlist nl) { Cell subCell = (Cell)no.getProto(); Netlist subNL = cni.getNetList(); boolean first = false; StringBuffer infstr = new StringBuffer(); for(int pass = 0; pass < 5; pass++) { for(Iterator<Export> it = subCell.getExports(); it.hasNext(); ) { Export e = it.next(); if (!matchesPass(e.getCharacteristic(), pass)) continue; int exportWidth = subNL.getBusWidth(e); for(int i=0; i<exportWidth; i++) { Network net = nl.getNetwork(no, e, i); // get connection boolean portNamed = false; for(Iterator<Connection> cIt = no.getNodeInst().getConnections(); cIt.hasNext(); ) { Connection con = cIt.next(); PortProto otherPP = con.getPortInst().getPortProto(); if (otherPP instanceof Export) otherPP = ((Export)otherPP).getEquivalent(); if (otherPP == e) { ArcInst ai = con.getArc(); if (ai.getProto().getFunction() != ArcProto.Function.NONELEC) { if (con.isNegated()) { Integer index; if (con.getEndIndex() == ArcInst.HEADEND) index = negatedHeads.get(ai); else index = negatedTails.get(ai); if (index != null) { if (first) infstr.append(", "); first = true; String sigName = "PINV" + index.intValue(); infstr.append(sigName); continue; } } } break; } } if (portNamed) continue; // write connection String sigName = addString(net.getName(), null); if (!net.isExported() && !net.getArcs().hasNext()) sigName = "open"; if (first) infstr.append(", "); first = true; infstr.append(sigName); } } } return infstr.toString(); } /** * Method to write actual signals that connect to a primitive instance. * @param no the primitive node. * @param special a code describing special features of the primitive. * @param negatedHeads map of arcs with negated head ends. * @param negatedTails map of arcs with negated tail ends. * @param nl the Netlist for the Cell containing the instance. * @return a string with the connection signals. */ private String addRealPortsPrim(Nodable no, int special, Map<ArcInst,Integer> negatedHeads, Map<ArcInst,Integer> negatedTails, Netlist nl) { NodeProto np = no.getProto(); boolean first = false; StringBuffer infstr = new StringBuffer(); for(int pass = 0; pass < 5; pass++) { for(Iterator<PortProto> it = np.getPorts(); it.hasNext(); ) { PortProto pp = it.next(); // ignore the bias port of 4-port transistors if (np == Schematics.tech().transistor4Node) { if (pp.getName().equals("b")) continue; } if (!matchesPass(pp.getCharacteristic(), pass)) continue; if (special == BLOCKMOSTRAN) { // ignore electrically connected ports boolean connected = false; for(Iterator<PortProto> oIt = np.getPorts(); oIt.hasNext(); ) { PrimitivePort oPp = (PrimitivePort)oIt.next(); if (oPp == pp) break; if (oPp.getTopology() == ((PrimitivePort)pp).getTopology()) { connected = true; break; } } if (connected) continue; } if (special == BLOCKPOSLOGIC || special == BLOCKBUFFER || special == BLOCKINVERTER || special == BLOCKNAND || special == BLOCKNOR || special == BLOCKXNOR) { // ignore ports not named "a" or "y" if (!pp.getName().equals("a") && !pp.getName().equals("y")) continue; } if (special == BLOCKFLOPTS || special == BLOCKFLOPDS) { // ignore ports not named "i1", "ck", "preset", or "q" if (!pp.getName().equals("i1") && !pp.getName().equals("ck") && !pp.getName().equals("preset") && !pp.getName().equals("q")) continue; } if (special == BLOCKFLOPTR || special == BLOCKFLOPDR) { // ignore ports not named "i1", "ck", "clear", or "q" if (!pp.getName().equals("i1") && !pp.getName().equals("ck") && !pp.getName().equals("clear") && !pp.getName().equals("q")) continue; } // if multiple connections, get them all if (pp.getBasePort().isIsolated()) { for(Iterator<Connection> cIt = no.getNodeInst().getConnections(); cIt.hasNext(); ) { Connection con = cIt.next(); if (con.getPortInst().getPortProto() != pp) continue; ArcInst ai = con.getArc(); ArcProto.Function fun = ai.getProto().getFunction(); if (fun == ArcProto.Function.NONELEC) continue; String sigName = "open"; Network net = nl.getNetwork(ai, 0); if (net != null) sigName = addString(net.describe(false), no.getParent()); if (con.isNegated()) { Integer index; if (con.getEndIndex() == ArcInst.HEADEND) index = negatedHeads.get(ai); else index = negatedTails.get(ai); if (index != null) sigName = "PINV" + index.intValue(); } if (first) infstr.append(", "); first = true; infstr.append(sigName); } continue; } // get connection boolean portNamed = false; for(Iterator<Connection> cIt = no.getNodeInst().getConnections(); cIt.hasNext(); ) { Connection con = cIt.next(); PortProto otherPP = con.getPortInst().getPortProto(); if (otherPP instanceof Export) otherPP = ((Export)otherPP).getEquivalent(); boolean aka = false; if (otherPP instanceof PrimitivePort && pp instanceof PrimitivePort) { if (((PrimitivePort)otherPP).getTopology() == ((PrimitivePort)pp).getTopology()) aka = true; } if (otherPP == pp || aka) { ArcInst ai = con.getArc(); if (ai.getProto().getFunction() != ArcProto.Function.NONELEC) { if (con.isNegated()) { Integer index; if (con.getEndIndex() == ArcInst.HEADEND) index = negatedHeads.get(ai); else index = negatedTails.get(ai); if (index != null) { if (first) infstr.append(", "); first = true; String sigName = "PINV" + index.intValue(); infstr.append(sigName); continue; } } int wid = nl.getBusWidth(ai); for(int i=0; i<wid; i++) { if (first) infstr.append(", "); first = true; Network subNet = nl.getNetwork(ai, i); String subNetName = getOneNetworkName(subNet); String sigName = addString(subNetName, no.getParent()); infstr.append(sigName); } portNamed = true; } break; } } if (portNamed) continue; for(Iterator<Export> eIt = no.getNodeInst().getExports(); eIt.hasNext(); ) { Export e = eIt.next(); PortProto otherPP = e.getOriginalPort().getPortProto(); if (otherPP instanceof Export) otherPP = ((Export)otherPP).getEquivalent(); if (otherPP == pp) { int wid = nl.getBusWidth(e); for(int i=0; i<wid; i++) { if (first) infstr.append(", "); first = true; Network subNet = nl.getNetwork(e, i); String subNetName = getOneNetworkName(subNet); infstr.append(addString(subNetName, no.getParent())); } portNamed = true; break; } } if (portNamed) continue; // port is not connected or an export if (first) infstr.append(", "); first = true; infstr.append("open"); } } return infstr.toString(); } /** * Method to return a list of signals connected to a primitive. * @param no the primitive Nodable being written. * @param special special situation for that Nodable. * If "special" is BLOCKPOSLOGIC, BLOCKBUFFER or BLOCKINVERTER, only include input port "a" and output port "y". * If "special" is BLOCKFLOPTS or BLOCKFLOPDS, only include input ports "i1", "ck", "preset" and output port "q". * If "special" is BLOCKFLOPTR or BLOCKFLOPDR, only include input ports "i1", "ck", "clear" and output port "q". */ private String addPortListPrim(Nodable no, int special) { // emit special flip-flop ports if (special == BLOCKFLOPTS || special == BLOCKFLOPDS) return "i1, ck, preset: in BIT; q: out BIT"; if (special == BLOCKFLOPTR || special == BLOCKFLOPDR) return "i1, ck, clear: in BIT; q: out BIT"; String before = ""; StringBuffer infstr = new StringBuffer(); PrimitiveNode pnp = (PrimitiveNode)no.getProto(); for(int pass = 0; pass < 5; pass++) { boolean didsome = false; for(Iterator<PrimitivePort> it = pnp.getPrimitivePorts(); it.hasNext(); ) { PrimitivePort pp = it.next(); if (!matchesPass(pp.getCharacteristic(), pass)) continue; String portName = pp.getName(); if (special == BLOCKPOSLOGIC || special == BLOCKBUFFER || special == BLOCKINVERTER) { // ignore ports not named "a" or "y" if (!portName.equals("a") && !portName.equals("y")) continue; } if (pp.getBasePort().isIsolated()) { int inst = 1; for(Iterator<Connection> cIt = no.getNodeInst().getConnections(); cIt.hasNext(); ) { Connection con = cIt.next(); if (con.getPortInst().getPortProto() != pp) continue; infstr.append(before); before = ", "; String exportName = addString(portName, null) + (inst++); infstr.append(exportName); } } else { infstr.append(before); before = ", "; infstr.append(addString(portName, null)); } didsome = true; } if (didsome) { if (pass == 0) { infstr.append(": in BIT"); } else if (pass == 1 || pass == 2 || pass == 3) { infstr.append(": out BIT"); } else { infstr.append(": inout BIT"); } before = "; "; } } return infstr.toString(); } /** * Method to construct a list of export names for a cell. * @param cni the cell information. * @return a list of export names for the Cell. */ private String addPortList(CellNetInfo cni) { String before = ""; StringBuffer infstr = new StringBuffer(); for(int pass = 0; pass < 5; pass++) { boolean didsome = false; for(Iterator<CellSignal> it = cni.getCellSignals(); it.hasNext(); ) { CellSignal cs = it.next(); Export e = cs.getExport(); if (e == null) continue; if (!matchesPass(e.getCharacteristic(), pass)) continue; infstr.append(before); before = ", "; infstr.append(addString(cs.getName(), null)); didsome = true; } if (didsome) { if (pass == 0) { infstr.append(": in BIT"); } else if (pass == 1 || pass == 2 || pass == 3) { infstr.append(": out BIT"); } else { infstr.append(": inout BIT"); } before = "; "; } } return infstr.toString(); } /****************************** SUPPORT ******************************/ /** * Method to determine whether a type of export goes in a particular pass of output. * Ports are written in 5 passes: input, output, power, ground, and everything else. * @param ch the PortCharacteristic of the port. * @param pass the pass number (0-4). * @return true of the given type of port goes in the given pass. */ private boolean matchesPass(PortCharacteristic ch, int pass) { switch (pass) { case 0: // must be an input port return ch == PortCharacteristic.IN; case 1: // must be an output port return ch == PortCharacteristic.OUT; case 2: // must be a power port return ch == PortCharacteristic.PWR; case 3: // must be a ground port return ch == PortCharacteristic.GND; } return ch != PortCharacteristic.IN && ch != PortCharacteristic.OUT && ch != PortCharacteristic.PWR && ch != PortCharacteristic.GND; } /** * Method to return a single name for a Network. * Choose the first if there are more than one. * @param net the Network to name. * @return the name of the Network. */ private String getOneNetworkName(Network net) { Iterator<String> nIt = net.getNames(); if (nIt.hasNext()) return nIt.next(); return net.describe(false); } /** * Class to determine the VHDL name and special factors for a node. */ private static class AnalyzePrimitive { private String primName; private int special; /** * Method to get the name of this analyzed primitive node. * @return the name of this analyzed primitive node. */ private String getPrimName() { return primName; } /** * Method to return the special code for this analyzed primitive node: * @return the special code for the analyzed primitive node:<BR> * BLOCKNORMAL: no special port arrangements necessary.<BR> * BLOCKMOSTRAN: only output ports that are not electrically connected.<BR> * BLOCKBUFFER: only include input port "a" and output port "y".<BR> * BLOCKPOSLOGIC: only include input port "a" and output port "y".<BR> * BLOCKINVERTER: only include input port "a" and output port "y".<BR> * BLOCKNAND: only include input port "a" and output port "y".<BR> * BLOCKNOR: only include input port "a" and output port "y".<BR> * BLOCKXNOR: only include input port "a" and output port "y".<BR> * BLOCKFLOPTS: only include input ports "i1", "ck", "preset" and output port "q".<BR> * BLOCKFLOPTR: only include input ports "i1", "ck", "clear" and output port "q".<BR> * BLOCKFLOPDS: only include input ports "i1", "ck", "preset" and output port "q".<BR> * BLOCKFLOPDR: only include input ports "i1", "ck", "clear" and output port "q".<BR> * BLOCKFLOP: include input ports "i1", "i2", "ck", "preset", "clear", and output ports "q" and "qb". */ private int getSpecial() { return special; } /** * Constructor which analyzes a primitive node. * @param no the primitive node. * @param negatedHeads map of arcs with negated head ends. * @param negatedTails map of arcs with negated tail ends. */ private AnalyzePrimitive(Nodable no, Map<ArcInst,Integer> negatedHeads, Map<ArcInst,Integer> negatedTails, VHDLPreferences vp) { // cell instances are easy special = BLOCKNORMAL; if (no.isCellInstance()) { primName = no.getProto().getName(); return; } NodeInst ni = no.getNodeInst(); // get the primitive function PrimitiveNode.Function k = ni.getFunction(); primName = null; if (k == PrimitiveNode.Function.TRADMOS || k == PrimitiveNode.Function.TRA4DMOS) { primName = "DMOStran"; special = BLOCKMOSTRAN; } else if (k.isNTypeTransistor()) { primName = "nMOStran"; Variable var = no.getVar(Simulation.WEAK_NODE_KEY); if (var != null) primName = "nMOStranWeak"; special = BLOCKMOSTRAN; } else if (k.isPTypeTransistor()) { primName = "PMOStran"; Variable var = no.getVar(Simulation.WEAK_NODE_KEY); if (var != null) primName = "PMOStranWeak"; special = BLOCKMOSTRAN; } else if (k == PrimitiveNode.Function.TRANPN || k == PrimitiveNode.Function.TRA4NPN) { primName = "NPNtran"; } else if (k == PrimitiveNode.Function.TRAPNP || k == PrimitiveNode.Function.TRA4PNP) { primName = "PNPtran"; } else if (k == PrimitiveNode.Function.TRANJFET || k == PrimitiveNode.Function.TRA4NJFET) { primName = "NJFET"; } else if (k == PrimitiveNode.Function.TRAPJFET || k == PrimitiveNode.Function.TRA4PJFET) { primName = "PJFET"; } else if (k == PrimitiveNode.Function.TRADMES || k == PrimitiveNode.Function.TRA4DMES) { primName = "DMEStran"; } else if (k == PrimitiveNode.Function.TRAEMES || k == PrimitiveNode.Function.TRA4EMES) { primName = "EMEStran"; } else if (k == PrimitiveNode.Function.FLIPFLOPRSMS || k == PrimitiveNode.Function.FLIPFLOPRSN || k == PrimitiveNode.Function.FLIPFLOPRSP) { primName = "rsff"; special = BLOCKFLOP; } else if (k == PrimitiveNode.Function.FLIPFLOPJKMS || k == PrimitiveNode.Function.FLIPFLOPJKN || k == PrimitiveNode.Function.FLIPFLOPJKP) { primName = "jkff"; special = BLOCKFLOP; } else if (k == PrimitiveNode.Function.FLIPFLOPDMS || k == PrimitiveNode.Function.FLIPFLOPDN || k == PrimitiveNode.Function.FLIPFLOPDP) { primName = "dsff"; special = BLOCKFLOPDS; for(Iterator<Connection> it = ni.getConnections(); it.hasNext(); ) { Connection con = it.next(); if (con.getPortInst().getPortProto().getName().equals("clear")) { primName = "drff"; special = BLOCKFLOPDR; break; } } } else if (k == PrimitiveNode.Function.FLIPFLOPTMS || k == PrimitiveNode.Function.FLIPFLOPTN || k == PrimitiveNode.Function.FLIPFLOPTP) { primName = "tsff"; special = BLOCKFLOPTS; for(Iterator<Connection> it = ni.getConnections(); it.hasNext(); ) { Connection con = it.next(); if (con.getPortInst().getPortProto().getName().equals("clear")) { primName = "trff"; special = BLOCKFLOPTR; break; } } } else if (k == PrimitiveNode.Function.BUFFER) { primName = vp.vhdlNames.get(Schematics.tech().bufferNode); int slashPos = primName.indexOf('/'); special = BLOCKBUFFER; for(Iterator<Connection> it = ni.getConnections(); it.hasNext(); ) { Connection con = it.next(); if (!con.getPortInst().getPortProto().getName().equals("y")) continue; if (con.isNegated()) { if (slashPos >= 0) primName = primName.substring(slashPos+1); special = BLOCKINVERTER; if (con.getEndIndex() == ArcInst.HEADEND) negatedHeads.remove(con.getArc()); else negatedTails.remove(con.getArc()); break; } } if (special == BLOCKBUFFER) { if (slashPos >= 0) primName = primName.substring(0, slashPos); } } else if (k == PrimitiveNode.Function.GATEAND) { primName = vp.vhdlNames.get(Schematics.tech().andNode); int slashPos = primName.indexOf('/'); int inPort = 0; Connection isNeg = null; for(Iterator<Connection> it = ni.getConnections(); it.hasNext(); ) { Connection con = it.next(); if (con.getPortInst().getPortProto().getName().equals("a")) inPort++; if (!con.getPortInst().getPortProto().getName().equals("y")) continue; if (con.isNegated()) isNeg = con; } if (isNeg != null) { if (slashPos >= 0) primName = primName.substring(slashPos+1); special = BLOCKNAND; if (isNeg.getEndIndex() == ArcInst.HEADEND) negatedHeads.remove(isNeg.getArc()); else negatedTails.remove(isNeg.getArc()); } else { if (slashPos >= 0) primName = primName.substring(0, slashPos); special = BLOCKPOSLOGIC; } primName += inPort; } else if (k == PrimitiveNode.Function.GATEOR) { primName = vp.vhdlNames.get(Schematics.tech().orNode); int slashPos = primName.indexOf('/'); int inPort = 0; Connection isNeg = null; for(Iterator<Connection> it = ni.getConnections(); it.hasNext(); ) { Connection con = it.next(); if (con.getPortInst().getPortProto().getName().equals("a")) inPort++; if (!con.getPortInst().getPortProto().getName().equals("y")) continue; if (con.isNegated()) isNeg = con; } if (isNeg != null) { if (slashPos >= 0) primName = primName.substring(slashPos+1); special = BLOCKNOR; if (isNeg.getEndIndex() == ArcInst.HEADEND) negatedHeads.remove(isNeg.getArc()); else negatedTails.remove(isNeg.getArc()); } else { if (slashPos >= 0) primName = primName.substring(0, slashPos); special = BLOCKPOSLOGIC; } primName += inPort; } else if (k == PrimitiveNode.Function.GATEXOR) { primName = vp.vhdlNames.get(Schematics.tech().xorNode); int slashPos = primName.indexOf('/'); int inPort = 0; Connection isNeg = null; for(Iterator<Connection> it = ni.getConnections(); it.hasNext(); ) { Connection con = it.next(); if (con.getPortInst().getPortProto().getName().equals("a")) inPort++; if (!con.getPortInst().getPortProto().getName().equals("y")) continue; if (con.isNegated()) isNeg = con; } if (isNeg != null) { if (slashPos >= 0) primName = primName.substring(slashPos+1); special = BLOCKXNOR; if (isNeg.getEndIndex() == ArcInst.HEADEND) negatedHeads.remove(isNeg.getArc()); else negatedTails.remove(isNeg.getArc()); } else { if (slashPos >= 0) primName = primName.substring(0, slashPos); special = BLOCKPOSLOGIC; } primName += inPort; } else if (k == PrimitiveNode.Function.MUX) { primName = vp.vhdlNames.get(Schematics.tech().muxNode); int inPort = 0; for(Iterator<Connection> it = ni.getConnections(); it.hasNext(); ) { Connection con = it.next(); if (con.getPortInst().getPortProto().getName().equals("a")) inPort++; } primName += inPort; } else if (k == PrimitiveNode.Function.CONPOWER) { primName = "power"; } else if (k == PrimitiveNode.Function.CONGROUND) { primName = "ground"; } if (primName == null) { // if the node has an export with power/ground, make it that for(Iterator<Export> it = ni.getExports(); it.hasNext(); ) { Export e = it.next(); if (e.isPower()) { primName = "power"; break; } if (e.isGround()) { primName = "ground"; break; } } } } } /** * Class to aggregate signal names and write them on length-limited lines. */ private class SignalNameLine { private boolean hasContent = false; public void addSignalName(String sigName) { if (!hasContent) { writeWidthLimited("\n"); writeWidthLimited(" signal "); } else { writeWidthLimited(", "); } hasContent = true; writeWidthLimited(sigName); } public void finish() { if (hasContent) writeWidthLimited(": BIT;\n"); } } /****************************** SUBCLASSED METHODS FOR THE TOPOLOGY ANALYZER ******************************/ /** * Method to adjust a cell name to be safe for Verilog output. * @param name the cell name. * @return the name, adjusted for Verilog output. */ protected String getSafeCellName(String name) { if (name.length() == 0) return name; char first = name.charAt(0); if (!Character.isLetter(first)) name = "E_" + name; for(int i=0; i<name.length(); i++) { char ch = name.charAt(i); if (Character.isLetterOrDigit(ch)) continue; if (ch == '_') continue; name = name.substring(0, i) + "_" + name.substring(i+1); } return name; } /** Method to tell the netlister to deal with all Cells. */ protected boolean skipCellAndSubcells(Cell cell) { return false; } /** Method to return the proper name of Power (just use whatever name is there) */ protected String getPowerName(Network net) { return net.getName(); } /** Method to return the proper name of Ground (just use whatever name is there) */ protected String getGroundName(Network net) { return net.getName(); } /** Method to return the proper name of a Global signal */ protected String getGlobalName(Global glob) { return "glbl." + glob.getName(); } /** * Method to tell the netlister that export names DO take precedence over * arc names when determining the name of the network. */ protected boolean isNetworksUseExportedNames() { return true; } /** Method to report that library names are not always prepended to cell names. */ protected boolean isLibraryNameAlwaysAddedToCellName() { return false; } /** Method to report that aggregate names (busses) are not used. */ protected boolean isAggregateNamesSupported() { return false; } /** Method to decide whether aggregate names (busses) can have gaps in their ranges. */ protected boolean isAggregateNameGapsSupported() { return false; } /** Method to tell netlister not to separate input and output names (this module does more detailed separation). */ protected boolean isSeparateInputAndOutput() { return false; } /** Method to tell the netlister to be case-sensitive. */ protected boolean isCaseSensitive() { return true; } /** Method to tell the netlister how to short resistors */ protected Netlist.ShortResistors getShortResistors() { return Netlist.ShortResistors.ALL; } /** Method to tell the netlister to mangle cell names that are parameterized. */ protected boolean canParameterizeNames() { return true; } /** * Method to adjust a network name to be safe for VHDL output. */ protected String getSafeNetName(String name, boolean bus) { return addString(name, null); } /** * Method to add the string "orig" to the infinite string. * If "environment" is not NONODEPROTO, it is the cell in which this signal is * to reside, and if that cell has nodes with this name, the signal must be renamed. */ private String addString(String orig, Cell environment) { // remove all nonVHDL characters while adding to current string StringBuffer sb = new StringBuffer(); boolean nonAlnum = false; for(int i=0; i<orig.length(); i++) { char chr = orig.charAt(i); if (Character.isLetterOrDigit(chr)) sb.append(chr); else { sb.append('_'); nonAlnum = true; } } // if there were nonalphanumeric characters, this cannot be a VHDL keyword if (!nonAlnum) { // check for VHDL keyword clashes if (CompileVHDL.isKeyword(orig) != null) { sb.append('_'); return sb.toString(); } // "bit" isn't a keyword, but the compiler can't handle it if (orig.equalsIgnoreCase("bit")) { sb.append('_'); return sb.toString(); } } // see if there is a name clash if (environment != null) { for(Iterator<NodeInst> it = environment.getNodes(); it.hasNext(); ) { NodeInst ni = it.next(); if (!ni.isCellInstance()) continue; if (orig.equals(ni.getProto().getName())) { sb.append('_'); break; } } } return sb.toString(); } }