/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: EDIF.java
* Input/output tool: EDIF netlist generator
* Original C Code written by Steven M. Rubin, B G West and Glen M. Lawson
* Translated to Java by Steven M. Rubin, Sun Microsystems.
*
* Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
*
* Electric(tm) is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.tool.io.output;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Export;
import com.sun.electric.database.hierarchy.Library;
import com.sun.electric.database.hierarchy.Nodable;
import com.sun.electric.database.hierarchy.View;
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.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.DisplayedText;
import com.sun.electric.database.variable.ElectricObject;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.technologies.Artwork;
import com.sun.electric.technology.technologies.Generic;
import com.sun.electric.technology.technologies.Schematics;
import com.sun.electric.tool.io.IOTool;
import com.sun.electric.tool.user.GraphicsPreferences;
import com.sun.electric.util.TextUtils;
import com.sun.electric.util.math.GenMath;
import com.sun.electric.util.math.Orientation;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This is the netlister for EDIF.
*/
public class EDIF extends Topology
{
/** true to add extra "ripping" cells where arcs and busses meet */ private static final boolean ADD_RIPPERS = true;
private static class EGraphic
{
private String text;
EGraphic(String text) { this.text = text; }
String getText() { return text; }
}
private static final EGraphic EGUNKNOWN = new EGraphic("UNKNOWN");
private static final EGraphic EGART = new EGraphic("ARTWORK");
private static final EGraphic EGWIRE = new EGraphic("WIRE");
private static final EGraphic EGBUS = new EGraphic("BUS");
private EGraphic egraphic = EGUNKNOWN;
private EGraphic egraphic_override = EGUNKNOWN;
// for bus rippers
private static class BusRipper
{
private NodeInst ni;
private Network net;
private int busWidth;
private int busIndex;
private int splitterIndex;
private String busName;
private static HashMap<Cell,List<BusRipper>> rippersPerCell;
public static void init()
{
rippersPerCell = null;
}
private BusRipper(NodeInst ni, Network net, int busWidth, int busIndex, int splitterIndex, String busName)
{
this.ni = ni;
this.net = net;
this.busWidth = busWidth;
this.busIndex = busIndex;
this.splitterIndex = splitterIndex;
this.busName = busName;
}
public int getBusWidth() { return busWidth; }
public int getBusIndex() { return busIndex; }
public int getSplitterIndex() { return splitterIndex; }
public static void makeBusRipper(NodeInst ni, Network net, int busWidth, int busIndex, int splitterIndex, String busName)
{
BusRipper br = new BusRipper(ni, net, busWidth, busIndex, splitterIndex, busName);
// add to lists
Cell cell = ni.getParent();
if (rippersPerCell == null) rippersPerCell = new HashMap<Cell,List<BusRipper>>();
List<BusRipper> rippersInCell = rippersPerCell.get(cell);
if (rippersInCell == null)
{
rippersInCell = new ArrayList<BusRipper>();
rippersPerCell.put(cell, rippersInCell);
}
rippersInCell.add(br);
}
public static BusRipper findBusRipper(NodeInst ni, Network net)
{
if (rippersPerCell == null) return null;
List<BusRipper> rippersInCell = rippersPerCell.get(ni.getParent());
if (rippersInCell == null) return null;
for(BusRipper br : rippersInCell)
{
if (br.ni == ni && br.net == net) return br;
}
return null;
}
public static List<BusRipper> getRippersOnBus(Cell cell, String busName)
{
List<BusRipper> ripperList = new ArrayList<BusRipper>();
if (rippersPerCell == null) return ripperList;
List<BusRipper> rippersInCell = rippersPerCell.get(cell);
if (rippersInCell == null) return ripperList;
for(BusRipper br : rippersInCell)
{
if (br.busName.equals(busName)) ripperList.add(br);
}
return ripperList;
}
}
// settings that may later be changed by preferences
private static final String primitivesLibName = "ELECTRIC_PRIMS";
private int scale = 20;
EDIFEquiv equivs;
private final HashMap<Library,LibToWrite> libsToWrite; // key is Library, Value is LibToWrite
private final List<Library> libsToWriteOrder; // list of libraries to write, in order
private EDIFPreferences localPrefs;
private static class LibToWrite {
private final List<CellToWrite> cellsToWrite;
private LibToWrite(Library l) {
cellsToWrite = new ArrayList<CellToWrite>();
}
private void add(CellToWrite c) { cellsToWrite.add(c); }
private Iterator<CellToWrite> getCells() { return cellsToWrite.iterator(); }
}
private static class CellToWrite {
private final Cell cell;
private final CellNetInfo cni;
private final VarContext context;
private CellToWrite(Cell cell, CellNetInfo cni, VarContext context) {
this.cell = cell;
this.cni = cni;
this.context = context;
}
}
public static class EDIFPreferences extends OutputPreferences
{
boolean edifUseSchematicView = IOTool.isFactoryEDIFUseSchematicView();
boolean edifCadenceCompatibility = IOTool.isFactoryEDIFCadenceCompatibility();
String configurationFile = IOTool.getEDIFConfigurationFile();
GraphicsPreferences gp;
public EDIFPreferences(boolean factory) {
super(factory);
edifUseSchematicView = factory ? IOTool.isFactoryEDIFUseSchematicView() : IOTool.isEDIFUseSchematicView();
edifCadenceCompatibility = factory ? IOTool.isFactoryEDIFCadenceCompatibility() : IOTool.isEDIFCadenceCompatibility();
gp = new GraphicsPreferences(factory);
}
public Output doOutput(Cell cell, VarContext context, String filePath)
{
EDIF out = new EDIF(this);
if (out.openTextOutputStream(filePath)) return out.finishWrite();
if (out.writeCell(cell, context)) return out.finishWrite();
if (out.closeTextOutputStream()) return out.finishWrite();
System.out.println(filePath + " written");
return out.finishWrite();
}
}
/**
* Creates a new instance of the EDIF netlister.
*/
EDIF(EDIFPreferences ep)
{
localPrefs = ep;
libsToWrite = new HashMap<Library,LibToWrite>();
libsToWriteOrder = new ArrayList<Library>();
equivs = new EDIFEquiv(localPrefs.configurationFile);
BusRipper.init();
}
protected void start()
{
// If this is a layout representation, then create the footprint
if (topCell.getView() == View.LAYOUT)
{
// default routing grid is 6.6u = 660 centimicrons
int rgrid = 660;
// calculate the actual routing grid in microns
double route = rgrid / 100;
// write the header
System.out.println("Writing footprint for cell " + makeToken(topCell.getName()));
printWriter.println("(footprint " + TextUtils.formatDouble(route) + "e-06");
printWriter.println(" (unknownLayoutRep");
// get standard cell dimensions
Rectangle2D cellBounds = topCell.getBounds();
double width = cellBounds.getWidth() / rgrid;
double height = cellBounds.getHeight() / rgrid;
printWriter.println(" (" + makeToken(topCell.getName()) + " standard (" +
TextUtils.formatDouble(height) + " " + TextUtils.formatDouble(width) + ")");
printWriter.println(")))");
return;
}
// write the header
String header = "Electric VLSI Design System";
if (localPrefs.includeDateAndVersionInOutput)
{
header += ", version " + Version.getVersion();
}
writeHeader(topCell, header, "EDIF Writer", topCell.getLibrary().getName());
// write out all primitives used in the library
blockOpen("library");
blockPutIdentifier(primitivesLibName);
blockPut("edifLevel", "0");
blockOpen("technology");
blockOpen("numberDefinition");
if (localPrefs.edifUseSchematicView)
{
writeScale(Technology.getCurrent());
}
blockClose("numberDefinition");
if (localPrefs.edifUseSchematicView)
{
writeFigureGroup(EGART);
writeFigureGroup(EGWIRE);
writeFigureGroup(EGBUS);
}
blockClose("technology");
HashMap<Object,PrimitiveNode> primsFound = new HashMap<Object,PrimitiveNode>();
writeAllPrims(topCell, primsFound);
blockClose("library");
// initialize rippers library
if (ADD_RIPPERS)
{
// figure out how many bus rippers are needed
HashSet<Integer> rippers = new HashSet<Integer>();
countRippers(topCell, rippers);
if (rippers.size() > 0)
{
blockOpen("library");
blockPutIdentifier("cdsRipLib");
blockPut("edifLevel", "0");
blockOpen("technology");
blockOpen("numberDefinition");
if (localPrefs.edifUseSchematicView)
writeScale(Technology.getCurrent());
blockClose("numberDefinition");
blockClose("technology");
}
for(Integer width : rippers)
{
blockOpen("cell");
blockPutIdentifier("ripper_" + width.intValue());
blockPut("cellType", "RIPPER");
blockOpen("view");
blockPutIdentifier("symbol");
blockPut("viewType", localPrefs.edifUseSchematicView ? "SCHEMATIC" : "NETLIST");
blockOpen("interface");
blockOpen("port");
blockOpen("array");
blockPutIdentifier("dst_0");
blockPutIdentifier(width.toString());
blockClose("array");
blockClose("port");
blockOpen("port");
blockOpen("array");
blockPutIdentifier("src");
blockPutIdentifier(width.toString());
blockClose("array");
blockClose("port");
blockOpen("joined");
blockPut("portRef", "dst_0");
blockPut("portRef", "src");
blockClose("joined");
blockOpen("symbol");
blockOpen("figure");
blockPutIdentifier("wire");
blockOpen("circle");
blockOpen("pt");
blockPutIdentifier("-5");
blockPutIdentifier("0");
blockClose("pt");
blockOpen("pt");
blockPutIdentifier("5");
blockPutIdentifier("0");
blockClose("pt");
blockClose("circle");
blockClose("figure");
blockOpen("portImplementation");
blockPutIdentifier("dst_0");
blockOpen("connectLocation");
blockOpen("figure");
blockPutIdentifier("pin");
blockOpen("dot");
writePoint(0, 0);
blockClose("dot");
blockClose("figure");
blockClose("connectLocation");
blockClose("portImplementation");
blockOpen("portImplementation");
blockPutIdentifier("src");
blockOpen("connectLocation");
blockOpen("figure");
blockPutIdentifier("pin");
blockOpen("dot");
writePoint(0, 0);
blockClose("dot");
blockClose("figure");
blockClose("connectLocation");
blockClose("portImplementation");
blockClose("symbol");
blockClose("interface");
blockClose("view");
blockClose("cell");
}
if (rippers.size() > 0)
{
blockClose("library");
}
}
// external libs
// organize by library
List<String> libs = new ArrayList<String>();
for (EDIFEquiv.NodeEquivalence e : equivs.getNodeEquivs()) {
if (libs.contains(e.externalLib)) continue;
libs.add(e.externalLib);
}
for (String lib : libs) {
blockOpen("external");
blockPutIdentifier(lib);
blockPut("edifLevel", "0");
blockOpen("technology");
blockOpen("numberDefinition");
if (localPrefs.edifUseSchematicView)
{
writeScale(Technology.getCurrent());
}
blockClose("technology");
for (EDIFEquiv.NodeEquivalence e : equivs.getNodeEquivs()) {
if (!lib.equals(e.externalLib)) continue;
String viewType = null;
if (e.exortedType != null) viewType = "GRAPHIC"; // pins must have GRAPHIC view
writeExternalDef(e.externalCell, e.externalView, viewType, e.getExtPorts());
}
blockClose("external");
}
}
/**
* Build up lists of cells that need to be written, organized by library
*/
protected void writeCellTopology(Cell cell, String cellName, CellNetInfo cni, VarContext context, Topology.MyCellInfo info)
{
Library lib = cell.getLibrary();
LibToWrite l = libsToWrite.get(lib);
if (l == null) {
l = new LibToWrite(lib);
libsToWrite.put(lib, l);
libsToWriteOrder.add(lib);
}
l.add(new CellToWrite(cell, cni, context));
}
protected void done()
{
// Note: if there are cross dependencies between libraries, there is no
// way to write out valid EDIF without changing the cell organization of the libraries
for (Library lib : libsToWriteOrder) {
LibToWrite l = libsToWrite.get(lib);
// here is where we write everything out, organized by library
// write library header
blockOpen("library");
blockPutIdentifier(makeToken(lib.getName()));
blockPut("edifLevel", "0");
blockOpen("technology");
blockOpen("numberDefinition", false);
if (localPrefs.edifUseSchematicView)
{
writeScale(Technology.getCurrent());
}
blockClose("numberDefinition");
if (localPrefs.edifUseSchematicView)
{
writeFigureGroup(EGART);
writeFigureGroup(EGWIRE);
writeFigureGroup(EGBUS);
}
blockClose("technology");
for (Iterator<CellToWrite> it2 = l.getCells(); it2.hasNext(); ) {
CellToWrite c = it2.next();
writeCellEdif(c.cell, c.cni, c.context);
}
blockClose("library");
}
// post-identify the design and library
blockOpen("design");
blockPutIdentifier(makeToken(topCell.getName()));
blockOpen("cellRef");
blockPutIdentifier(makeToken(topCell.getName()));
blockPut("libraryRef", makeToken(topCell.getLibrary().getName()));
// clean up
blockFinish();
}
/**
* Method to write cellGeom
*/
private void writeCellEdif(Cell cell, CellNetInfo cni, VarContext context)
{
// write out the cell header information
blockOpen("cell");
blockPutIdentifier(makeToken(cell.getName()));
blockPut("cellType", "generic");
blockOpen("view");
blockPutIdentifier("symbol");
blockPut("viewType", localPrefs.edifUseSchematicView ? "SCHEMATIC" : "NETLIST");
// write out the interface description
blockOpen("interface");
// write ports and directions
Netlist netList = cni.getNetList();
HashMap<Export,String> busExports = new HashMap<Export,String>();
for(Iterator<CellSignal> it = cni.getCellSignals(); it.hasNext(); )
{
CellSignal cs = it.next();
if (cs.isExported())
{
Export e = cs.getExport();
String direction = "INPUT";
if (e.getCharacteristic() == PortCharacteristic.OUT ||
e.getCharacteristic() == PortCharacteristic.REFOUT) direction = "OUTPUT";
if (e.getCharacteristic() == PortCharacteristic.BIDIR) direction = "INOUT";
int busWidth = netList.getBusWidth(e);
if (busWidth > 1)
{
// only write bus exports once
if (busExports.get(e) != null) continue;
blockOpen("port");
blockOpen("array");
String eBusName = convertBusName(e.getName(), netList, e);
String busName = makeToken(eBusName);
busExports.put(e, busName);
blockOpen("rename");
blockPutIdentifier(busName);
blockPutString(eBusName);
blockClose("rename");
blockPutIdentifier(Integer.toString(busWidth));
blockClose("array");
blockPut("direction", direction);
blockClose("port");
} else
{
blockOpen("port");
blockPutIdentifier(makeToken(cs.getName()));
blockPut("direction", direction);
blockClose("port");
}
}
}
if (localPrefs.edifUseSchematicView)
{
for (Iterator<Variable> it = cell.getParametersAndVariables(); it.hasNext(); ) {
Variable var = it.next();
if (var.getTrueName().equals("prototype_center")) continue;
blockOpen("property");
String name = var.getTrueName();
String name2 = makeValidName(name);
if (!name.equals(name2)) {
blockOpen("rename", false);
blockPutIdentifier(name2);
blockPutString(name);
blockClose("rename");
} else {
blockPutIdentifier(name);
}
blockOpen("string", false);
String value = var.getObject().toString();
value = value.replaceAll("\"", "%34%");
blockPutString(value);
blockClose("string");
if (!var.isAttribute()) {
blockOpen("owner", false);
blockPutString("Electric");
}
blockClose("property");
}
// output the icon
//writeIcon(cell);
writeSymbol(cell);
}
blockClose("interface");
// write cell contents
blockOpen("contents");
if (localPrefs.edifUseSchematicView) {
blockOpen("page");
blockPutIdentifier("SH1");
}
// add ripper instances
if (ADD_RIPPERS)
{
int splitterIndex = 1;
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (ni.isCellInstance()) continue;
if (equivs.getNodeEquivalence(ni) != null) continue; // will be defined by external reference
PrimitiveNode.Function fun = ni.getFunction();
if (!fun.isPin()) continue;
// check all the connections
ArcInst busFound = null;
for(Iterator<Connection> cIt = ni.getConnections(); cIt.hasNext(); )
{
Connection con = cIt.next();
ArcInst ai = con.getArc();
int width = netList.getBusWidth(ai);
if (width > 1) busFound = ai;
}
if (busFound == null) continue;
int busWidth = netList.getBusWidth(busFound);
// a bus pin: look for wires that indicate ripping
for(Iterator<Connection> cIt = ni.getConnections(); cIt.hasNext(); )
{
Connection con = cIt.next();
ArcInst ai = con.getArc();
int width = netList.getBusWidth(ai);
if (width < 2)
{
// add an instance of a ripper
Network net = netList.getNetwork(ai, 0);
int busIndex = 0;
for(int i=0; i<busWidth; i++)
{
Network busNet = netList.getNetwork(busFound, i);
if (busNet == net)
{
busIndex = i;
break;
}
}
BusRipper.makeBusRipper(ni, net, busWidth, busIndex, splitterIndex,
netList.getBusName(busFound).toString());
blockOpen("instance");
String splitterName = "splitter_" + splitterIndex;
splitterIndex++;
blockPutIdentifier(splitterName);
blockOpen("viewRef");
blockPutIdentifier("symbol");
blockOpen("cellRef");
blockPutIdentifier("ripper_" + busWidth);
blockPut("libraryRef", "cdsRipLib");
blockClose("cellRef");
blockClose("viewRef");
blockOpen("transform");
blockOpen("origin");
writePoint(ni.getAnchorCenterX(), ni.getAnchorCenterY());
blockClose("origin");
blockClose("transform");
blockClose("instance");
}
}
}
}
for(Iterator<NodeInst> nIt = cell.getNodes(); nIt.hasNext(); )
{
NodeInst no = nIt.next();
if (no.isCellInstance()) {
Cell c = (Cell)no.getProto();
if (cell.iconView() == c) continue; // can't make instance of icon view
}
if (!no.isCellInstance())
{
PrimitiveNode.Function fun = no.getFunction();
Variable var = no.getVar(Artwork.ART_MESSAGE);
if (var != null) {
// this is cell annotation text
blockOpen("commentGraphics");
blockOpen("annotate", false);
Point2D point = new Point2D.Double(no.getAnchorCenterX(), no.getAnchorCenterY());
Poly p = new Poly(new Point2D [] { point });
String s;
if (var.getObject() instanceof String []) {
String [] lines = (String [])var.getObject();
StringBuffer sbuf = new StringBuffer();
for (int i=0; i<lines.length; i++) {
sbuf.append(lines[i]);
if (i != lines.length-1) sbuf.append("%10%"); // newline separator in Cadence
}
s = sbuf.toString();
} else {
s = var.getObject().toString();
}
p.setString(s);
p.setTextDescriptor(var.getTextDescriptor());
p.setStyle(var.getTextDescriptor().getPos().getPolyType());
writeSymbolPoly(p, null, 1);
blockClose("commentGraphics");
continue;
}
if (fun == PrimitiveNode.Function.UNKNOWN || fun.isPin() ||
fun.isContact() || fun == PrimitiveNode.Function.NODE ||
//fun == PrimitiveNode.Function.CONNECT ||
fun == PrimitiveNode.Function.ART) continue;
}
String iname = makeComponentName(no);
String oname = no.getName();
// write reference - get lib, cell, view
String refLib = "?", refCell="?", refView = "symbol";
int addedRotation = 0;
boolean openedPortImplementation = false;
EDIFEquiv.NodeEquivalence ne = equivs.getNodeEquivalence(no);
if (ne != null) {
addedRotation = ne.rotation * 10;
refLib = ne.externalLib;
refCell = ne.externalCell;
if (ne.exortedType != null) {
// cadence pin: encapsulate instance inside of a portImplementation
Iterator<Export> eit = no.getExports();
if (eit.hasNext()) {
Export e = eit.next();
oname = e.getName();
writePortImplementation(e, false);
openedPortImplementation = true;
}
}
} else if (!no.isCellInstance())
{
NodeInst ni = no;
PrimitiveNode.Function fun = ni.getFunction();
// do default action for primitives
refLib = primitivesLibName;
if (fun == PrimitiveNode.Function.GATEAND || fun == PrimitiveNode.Function.GATEOR || fun == PrimitiveNode.Function.GATEXOR) { // count the number of inputs
int i = 0;
for(Iterator<Connection> pIt = ni.getConnections(); pIt.hasNext(); )
{
Connection con = pIt.next();
if (con.getPortInst().getPortProto().getName().equals("a")) i++;
}
refCell = makeToken(ni.getProto().getName()) + i;
} else {
refCell = describePrimitive(ni, fun);
}
} else
{
// this is a cell
Cell np = (Cell)no.getProto();
refLib = np.getLibrary().getName();
refCell = makeToken(no.getProto().getName());
}
// write reference
blockOpen("instance");
if (!oname.equalsIgnoreCase(iname))
{
blockOpen("rename", false);
blockPutIdentifier(iname);
blockPutString(oname);
blockClose("rename");
} else blockPutIdentifier(iname);
blockOpen("viewRef");
blockPutIdentifier(refView);
blockOpen("cellRef", false);
blockPutIdentifier(refCell);
blockPut("libraryRef", refLib);
blockClose("viewRef");
// now graphical information
if (localPrefs.edifUseSchematicView)
{
NodeInst ni = no;
blockOpen("transform");
// get the orientation (note only support orthogonal)
blockPut("orientation", getOrientation(ni, addedRotation));
// now the origin
blockOpen("origin");
double cX = ni.getAnchorCenterX(), cY = ni.getAnchorCenterY();
Point2D pt = new Point2D.Double(cX, cY);
writePoint(pt.getX(), pt.getY());
blockClose("transform");
}
// check for variables to write as properties
if (localPrefs.edifUseSchematicView)
{
// do all display variables first
NodeInst ni = no;
Poly[] varPolys = ni.getDisplayableVariables(ni.getBounds(), null, false, localPrefs.gp.isShowTempNames());
writeDisplayableVariables(varPolys, ni.rotateOut());
}
blockClose("instance");
if (openedPortImplementation) {
blockClose("portImplementation");
}
}
// if there is anything to connect, write the networks in the cell
for(Iterator<CellSignal> it = cni.getCellSignals(); it.hasNext(); )
{
CellSignal cs = it.next();
// ignore unconnected (unnamed) nets
String netName = cs.getNetwork().describe(false);
if (netName.length() == 0) continue;
// establish if this is a global net
boolean globalport = false;
blockOpen("net");
netName = cs.getName();
String eName = makeToken(netName);
if (globalport)
{
blockOpen("rename");
blockPutIdentifier(eName);
blockPutString(eName + "!");
blockClose("rename");
blockPut("property", "GLOBAL");
} else
{
EDIFEquiv.GlobalEquivalence ge = equivs.getElectricGlobalEquivalence(netName);
if (ge != null) netName = ge.externGName;
if (!eName.equals(netName))
{
// different names
blockOpen("rename");
blockPutIdentifier(eName);
blockPutString(netName);
blockClose("rename");
} else blockPutIdentifier(eName);
}
// write net connections
blockOpen("joined");
// include exported ports
if (cs.isExported())
{
Export e = cs.getExport();
if (netList.getBusWidth(e) <= 1)
{
String pt = e.getName();
blockPut("portRef", makeToken(pt));
}
}
Network net = cs.getNetwork();
for(Iterator<Nodable> nIt = netList.getNodables(); nIt.hasNext(); )
{
Nodable no = nIt.next();
NodeProto niProto = no.getProto();
// mention connectivity to a bus ripper
if (no instanceof NodeInst)
{
BusRipper br = BusRipper.findBusRipper((NodeInst)no, net);
if (br != null)
{
blockOpen("portRef");
blockOpen("member");
blockPutIdentifier("dst_0");
blockPutIdentifier(Integer.toString(br.getBusIndex()));
blockClose("member");
blockOpen("instanceRef");
blockPutIdentifier("splitter_" + br.getSplitterIndex());
blockClose("instanceRef");
blockClose("portRef");
}
}
EDIFEquiv.NodeEquivalence ne = equivs.getNodeEquivalence(no.getNodeInst());
if (niProto instanceof Cell)
{
String nodeName = parameterizedName(no, context);
CellNetInfo subCni = getCellNetInfo(nodeName);
for(Iterator<CellSignal> sIt = subCni.getCellSignals(); sIt.hasNext(); )
{
CellSignal subCs = sIt.next();
// ignore networks that aren't exported
PortProto subPp = subCs.getExport();
if (subPp == null) continue;
// single signal
Network subNet = netList.getNetwork(no, subPp, subCs.getExportIndex());
if (cs != cni.getCellSignal(subNet)) continue;
String portName = subCs.getName();
if (ne != null) {
// get equivalent port name
EDIFEquiv.PortEquivalence pe = ne.getPortEquivElec(portName);
if (pe == null) {
String msg = "Error: no equivalent port found for '"+portName+"' on node "+niProto.describe(false) +
"\n Equivalence class: " + ne.toString();
reportError(msg);
} else {
if (!pe.getExtPort().ignorePort) {
blockOpen("portRef");
portName = pe.getExtPort().name;
blockPutIdentifier(makeToken(portName));
blockPut("instanceRef", makeComponentName(no));
blockClose("portRef");
}
}
} else {
blockOpen("portRef");
blockPutIdentifier(makeToken(portName));
blockPut("instanceRef", makeComponentName(no));
blockClose("portRef");
}
}
} else
{
NodeInst ni = (NodeInst)no;
PrimitiveNode.Function fun = ni.getFunction();
if (fun == PrimitiveNode.Function.UNKNOWN || fun.isPin() ||
fun.isContact() || fun == PrimitiveNode.Function.NODE ||
//fun == PrimitiveNode.Function.CONNECT ||
fun == PrimitiveNode.Function.ART) continue;
for(Iterator<Connection> cIt = ni.getConnections(); cIt.hasNext(); )
{
Connection con = cIt.next();
ArcInst ai = con.getArc();
Network aNet = netList.getNetwork(ai, 0);
if (aNet != net) continue;
String portName = con.getPortInst().getPortProto().getName();
if (ne != null) {
// get equivalent port name
EDIFEquiv.PortEquivalence pe = ne.getPortEquivElec(portName);
if (pe == null) {
String msg = "Error: no equivalent port found for '"+portName+"' on node "+niProto.describe(false) +
"\n Equivalence class: " + ne.toString();
reportError(msg);
} else {
if (!pe.getExtPort().ignorePort) {
blockOpen("portRef");
portName = pe.getExtPort().name;
String safeName = makeValidName(portName);
if (!safeName.equals(portName)) {
blockPutIdentifier(makeToken(safeName));
} else {
blockPutIdentifier(makeToken(portName));
}
blockPut("instanceRef", makeComponentName(no));
blockClose("portRef");
}
}
} else {
blockOpen("portRef");
blockPutIdentifier(makeToken(portName));
blockPut("instanceRef", makeComponentName(no));
blockClose("portRef");
}
}
}
}
blockClose("joined");
if (localPrefs.edifUseSchematicView)
{
// output net graphic information for all arc instances connected to this net
egraphic = EGUNKNOWN;
egraphic_override = EGWIRE;
for(Iterator<ArcInst> aIt = cell.getArcs(); aIt.hasNext(); )
{
ArcInst ai = aIt.next();
int aWidth = netList.getBusWidth(ai);
if (aWidth > 1) continue;
Network aNet = netList.getNetwork(ai, 0);
if (aNet == net) writeSymbolArcInst(ai, GenMath.MATID);
}
setGraphic(EGUNKNOWN);
egraphic_override = EGUNKNOWN;
}
if (cs.isExported()) {
Export e = cs.getExport();
blockOpen("comment");
blockPutString("exported as "+e.getName()+", type "+e.getCharacteristic().getName());
blockClose("comment");
}
if (globalport)
blockPut("userData", "global");
blockClose("net");
}
// write busses
if (ADD_RIPPERS)
{
// the new way
HashSet<ArcInst> bussesSeen = new HashSet<ArcInst>();
for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
int busWidth = netList.getBusWidth(ai);
if (busWidth < 2) continue;
if (bussesSeen.contains(ai)) continue;
blockOpen("net");
blockOpen("array");
String realBusName = netList.getBusName(ai).toString();
String busName = convertBusName(realBusName, netList, ai);
String oname = makeToken(busName);
if (!oname.equals(busName))
{
// different names
blockOpen("rename");
blockPutIdentifier(oname);
blockPutString(busName);
blockClose("rename");
} else blockPutIdentifier(oname);
blockPutIdentifier(Integer.toString(busWidth));
blockClose("array");
// now each sub-net name
blockOpen("joined");
List<BusRipper> rippersOnBus = BusRipper.getRippersOnBus(cell, realBusName);
for(BusRipper br : rippersOnBus)
{
blockOpen("portList");
for(int i=0; i<busWidth; i++)
{
blockOpen("portRef");
blockOpen("member");
blockPutIdentifier("src");
blockPutIdentifier(Integer.toString(i));
blockClose("member");
blockOpen("instanceRef");
blockPutIdentifier("splitter_" + br.splitterIndex);
blockClose("instanceRef");
blockClose("portRef");
}
blockClose("portList");
}
blockClose("joined");
// now graphics for the bus
if (localPrefs.edifUseSchematicView)
{
// output net graphic information for all arc instances connected to this net
egraphic = EGUNKNOWN;
egraphic_override = EGBUS;
for(Iterator<ArcInst> aIt = cell.getArcs(); aIt.hasNext(); )
{
ArcInst oAi = aIt.next();
if (oAi.getProto() != Schematics.tech().bus_arc) continue;
String arcBusName = netList.getBusName(oAi).toString();
if (arcBusName.equals(realBusName))
{
writeSymbolArcInst(oAi, GenMath.MATID);
bussesSeen.add(oAi);
}
}
setGraphic(EGUNKNOWN);
egraphic_override = EGUNKNOWN;
}
blockClose("net");
}
} else
{
// the old way: no longer done
for(Iterator<CellAggregateSignal> it = cni.getCellAggregateSignals(); it.hasNext(); )
{
CellAggregateSignal cas = it.next();
// ignore single signals
if (cas.getLowIndex() > cas.getHighIndex()) continue;
blockOpen("netBundle");
String busName = cas.getNameWithIndices();
String oname = makeToken(busName);
if (!oname.equals(busName))
{
// different names
blockOpen("rename");
blockPutIdentifier(oname);
blockPutString(busName);
blockClose("rename");
} else blockPutIdentifier(oname);
blockOpen("listOfNets");
// now each sub-net name
int numSignals = cas.getHighIndex() - cas.getLowIndex() + 1;
for (int k=0; k<numSignals; k++)
{
blockOpen("net");
// now output this name
CellSignal cs = cas.getSignal(k);
String pt = cs.getName();
oname = makeToken(pt);
if (!oname.equals(pt))
{
// different names
blockOpen("rename");
blockPutIdentifier(oname);
blockPutString(pt);
blockClose("rename");
} else blockPutIdentifier(oname);
blockClose("net");
}
// now graphics for the bus
if (localPrefs.edifUseSchematicView)
{
// output net graphic information for all arc instances connected to this net
egraphic = EGUNKNOWN;
egraphic_override = EGBUS;
for(Iterator<ArcInst> aIt = cell.getArcs(); aIt.hasNext(); )
{
ArcInst ai = aIt.next();
if (ai.getProto() != Schematics.tech().bus_arc) continue;
String arcBusName = netList.getBusName(ai).toString();
if (arcBusName.equals(busName)) writeSymbolArcInst(ai, GenMath.MATID);
}
setGraphic(EGUNKNOWN);
egraphic_override = EGUNKNOWN;
}
blockClose("netBundle");
continue;
}
}
if (localPrefs.edifUseSchematicView) {
blockClose("page");
}
blockClose("contents");
// matches "(cell "
blockClose("cell");
}
/****************************** MIDDLE-LEVEL HELPER METHODS ******************************/
private void writeHeader(Cell cell, String program, String comment, String origin)
{
// output the standard EDIF 2 0 0 header
blockOpen("edif");
blockPutIdentifier(cell.getName());
blockPut("edifVersion", "2 0 0");
blockPut("edifLevel", "0");
blockOpen("keywordMap");
blockPut("keywordLevel", "0"); // was "1"
blockClose("keywordMap");
blockOpen("status");
blockOpen("written");
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy MM dd HH mm ss");
blockPut("timeStamp", simpleDateFormat.format(new Date()));
if (program != null) blockPut("program", "\"" + program + "\"");
if (comment != null) blockPut("comment", "\"" + comment + "\"");
if (origin != null) blockPut("dataOrigin", "\"" + origin + "\"");
blockClose("status");
}
/**
* Method to dump the description of primitive "np" to the EDIF file
* If the primitive is a schematic gate, use "i" as the number of inputs
*/
private void writePrimitive(PrimitiveNode pn, int i, PrimitiveNode.Function fun)
{
// write primitive name
if (fun == PrimitiveNode.Function.GATEAND || fun == PrimitiveNode.Function.GATEOR || fun == PrimitiveNode.Function.GATEXOR)
{
blockOpen("cell");
String name = makeToken(pn.getName()) + i;
blockPutIdentifier(name);
} else
{
blockOpen("cell");
blockPutIdentifier(makeToken(pn.getName()));
}
// write primitive connections
blockPut("cellType", "generic");
blockOpen("comment");
Technology tech = pn.getTechnology();
blockPutString("Tech: "+tech.getTechName()+", Node: "+pn.getName()+", Func: "+fun.getConstantName());
blockClose("comment");
blockOpen("view");
blockPutIdentifier("symbol");
blockPut("viewType", localPrefs.edifUseSchematicView ? "SCHEMATIC" : "NETLIST");
blockOpen("interface");
int firstPortIndex = 0;
if (fun == PrimitiveNode.Function.GATEAND || fun == PrimitiveNode.Function.GATEOR || fun == PrimitiveNode.Function.GATEXOR)
{
for (int j = 0; j < i; j++)
{
blockOpen("port");
String name = "IN" + (j + 1);
blockPutIdentifier(name);
blockPut("direction", "INPUT");
blockClose("port");
}
firstPortIndex = 1;
}
for(int k=firstPortIndex; k<pn.getNumPorts(); k++)
{
PortProto pp = pn.getPort(k);
String direction = "input";
if (pp.getCharacteristic() == PortCharacteristic.OUT) direction = "output"; else
if (pp.getCharacteristic() == PortCharacteristic.BIDIR) direction = "inout";
blockOpen("port");
blockPutIdentifier(makeToken(pp.getName()));
blockPut("direction", direction);
blockClose("port");
}
NodeInst ni = NodeInst.makeDummyInstance(pn);
writeSymbol(pn, ni);
blockClose("cell");
}
// ports is a list of EDIFEquiv.Port objects
private void writeExternalDef(String extCell, String extView, String viewType, List<EDIFEquiv.Port> ports)
{
blockOpen("cell");
blockPutIdentifier(extCell);
blockPut("cellType", "generic");
blockOpen("view");
blockPutIdentifier(extView);
if (viewType == null) viewType = localPrefs.edifUseSchematicView ? "SCHEMATIC" : "NETLIST";
blockPut("viewType", viewType);
// write interface
blockOpen("interface");
for (EDIFEquiv.Port port : ports) {
if (port.ignorePort) continue;
blockOpen("port");
String safeName = makeValidName(port.name);
if (!safeName.equals(port.name)) {
blockOpen("rename");
blockPutIdentifier(safeName);
blockPutString(port.name);
blockClose("rename");
} else {
blockPutIdentifier(port.name);
}
blockClose("port");
}
blockClose("cell");
}
/**
* Method to count the usage of primitives hierarchically below cell "np"
*/
private void writeAllPrims(Cell cell, HashMap<Object,PrimitiveNode> primsFound)
{
// do not search this cell if it is an icon
if (cell.isIcon()) return;
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
NodeProto np = ni.getProto();
if (!ni.isCellInstance())
{
if (equivs.getNodeEquivalence(ni) != null) continue; // will be defined by external reference
PrimitiveNode pn = (PrimitiveNode)np;
PrimitiveNode.Function fun = ni.getFunction();
int i = 1;
if (fun == PrimitiveNode.Function.GATEAND || fun == PrimitiveNode.Function.GATEOR || fun == PrimitiveNode.Function.GATEXOR)
{
// count the number of inputs
for(Iterator<Connection> cIt = ni.getConnections(); cIt.hasNext(); )
{
Connection con = cIt.next();
if (con.getPortInst().getPortProto().getName().equals("a")) i++;
}
}
if (primsFound.get(getPrimKey(ni, i)) != null) continue; // already written
if (fun == PrimitiveNode.Function.UNKNOWN || fun.isPin() || fun == PrimitiveNode.Function.ART)
continue;
writePrimitive(pn, i, fun);
primsFound.put(getPrimKey(ni, i), pn);
continue;
}
// ignore recursive references (showing icon in contents)
if (ni.isIconOfParent()) continue;
// get actual subcell (including contents/body distinction)
Cell oNp = ((Cell)np).contentsView();
if (oNp == null) oNp = (Cell)np;
// search the subcell
writeAllPrims(oNp, primsFound);
}
}
/**
* Method to count the usage of primitives hierarchically below cell "np"
*/
private void countRippers(Cell cell, HashSet<Integer> rippers)
{
// do not search this cell if it is an icon
if (cell.isIcon()) return;
Netlist netlist = null;
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
NodeProto np = ni.getProto();
if (!ni.isCellInstance())
{
if (equivs.getNodeEquivalence(ni) != null) continue; // will be defined by external reference
PrimitiveNode.Function fun = ni.getFunction();
if (fun.isPin())
{
// check all the connections
int busWidthFound = -1;
boolean wireFound = false;
for(Iterator<Connection> cIt = ni.getConnections(); cIt.hasNext(); )
{
Connection con = cIt.next();
ArcInst ai = con.getArc();
if (netlist == null) netlist = cell.getNetlist();
int width = netlist.getBusWidth(ai);
if (width > 1) busWidthFound = width; else
wireFound = true;
}
if (wireFound && busWidthFound > 1)
rippers.add(new Integer(busWidthFound));
}
continue;
}
// ignore recursive references (showing icon in contents)
if (ni.isIconOfParent()) continue;
// get actual subcell (including contents/body distinction)
Cell oNp = ((Cell)np).contentsView();
if (oNp == null) oNp = (Cell)np;
// search the subcell
countRippers(oNp, rippers);
}
}
private Object getPrimKey(NodeInst ni, int i) {
if (ni.isCellInstance()) return null;
PrimitiveNode pn = (PrimitiveNode)ni.getProto();
PrimitiveNode.Function func = ni.getFunction();
String key = pn.getTechnology().getTechShortName() + "_" + pn.getName() + "_" + func.getConstantName() + "_" +i;
return key;
}
/**
* Method to generate a pt symbol (pt x y)
*/
private void writePoint(double x, double y)
{
blockOpen("pt");
blockPutInteger(scaleValue(x));
blockPutInteger(scaleValue(y));
blockClose("pt");
}
/**
* Method to convert a bus name to the proper string for output.
* @param busName the bus name in Electric.
* @return the bus name for EDIF output.
*/
private String convertBusName(String busName, Netlist netlist, ElectricObject eObj)
{
if (localPrefs.edifCadenceCompatibility)
{
// see if the bus is simple
int firstOpen = busName.indexOf('[');
if (firstOpen < 0) return busName;
boolean simple = true;
if (busName.indexOf('[', firstOpen+1) >= 0) simple = false; else
{
int closePos = busName.indexOf(']', firstOpen);
for(int i=firstOpen+1; i<closePos; i++)
{
char ch = busName.charAt(i);
if (ch != ':' && ch != ',' && !TextUtils.isDigit(ch))
{
simple = false;
break;
}
}
}
if (simple)
{
busName = busName.replaceAll("\\[", "\\<").replaceAll("\\]", "\\>");
} else
{
// complex name: break it into many signals
busName = "";
if (eObj instanceof ArcInst)
{
ArcInst ai = (ArcInst)eObj;
int width = netlist.getBusWidth(ai);
for(int i=0; i<width; i++)
{
Network net = netlist.getNetwork(ai, i);
if (busName.length() > 0) busName += ",";
Iterator<String> nIt = net.getNames();
String netName;
if (nIt.hasNext()) netName = nIt.next(); else
netName = net.describe(true);
busName += netName.replaceAll("\\[", "_").replaceAll("\\]", "_");
}
} else if (eObj instanceof Export)
{
Export e = (Export)eObj;
int width = netlist.getBusWidth(e);
for(int i=0; i<width; i++)
{
Network net = netlist.getNetwork(e, i);
if (busName.length() > 0) busName += ",";
Iterator<String> nIt = net.getNames();
String netName;
if (nIt.hasNext()) netName = nIt.next(); else
netName = net.describe(true);
busName += netName.replaceAll("\\[", "_").replaceAll("\\]", "_");
}
}
}
}
return busName;
}
/**
* Method to map Electric orientations to EDIF orientations
*/
public static String getOrientation(NodeInst ni, int addedRotation)
{
String orientation = "ERROR";
int angle = (ni.getAngle() - addedRotation);
if (angle < 0) angle = angle + 3600;
if (angle > 3600) angle = angle % 3600;
switch (angle)
{
case 0: orientation = ""; break;
case 900: orientation = "R90"; break;
case 1800: orientation = "R180"; break;
case 2700: orientation = "R270"; break;
}
if (ni.isMirroredAboutXAxis()) orientation = "MX" + orientation;
if (ni.isMirroredAboutYAxis()) orientation = "MY" + orientation;
if (orientation.length() == 0) orientation = "R0";
if (orientation.equals("MXR180")) orientation = "MY";
if (orientation.equals("MYR180")) orientation = "MX";
if (orientation.equals("MXR270")) orientation = "MYR90";
if (orientation.equals("MYR270")) orientation = "MXR90";
return orientation;
}
/**
* Method to scale the requested integer
* returns the scaled value
*/
private double scaleValue(double val)
{
return (int)(val*scale);
}
/**
* Method to properly identify an instance of a primitive node
* for ASPECT netlists
*/
private String describePrimitive(NodeInst ni, PrimitiveNode.Function fun)
{
if (fun.isResistor()) /* == PrimitiveNode.Function.RESIST)*/ return "Resistor";
if (fun == PrimitiveNode.Function.TRANPN) return "npn";
if (fun == PrimitiveNode.Function.TRAPNP) return "pnp";
if (fun.isNTypeTransistor()) return "nfet";
if (fun.isPTypeTransistor()) return "pfet";
if (fun == PrimitiveNode.Function.SUBSTRATE) return "gtap";
return makeToken(ni.getProto().getName());
}
/**
* Helper name builder
*/
private String makeComponentName(Nodable no)
{
String okname = makeValidName(no.getName());
if (okname.length() > 0)
{
char chr = okname.charAt(0);
if (TextUtils.isDigit(chr) || chr == '_')
okname = "&" + okname;
}
return okname;
}
/**
* Method to return null if there is no valid name in "var", corrected name if valid.
*/
public static String makeValidName(String name)
{
StringBuffer iptr = new StringBuffer(name);
// allow '&' for the first character (this must be fixed later if digit or '_')
int i = 0;
if (iptr.charAt(i) == '&') i++;
// allow "_" and alphanumeric for others
for(; i<iptr.length(); i++)
{
if (TextUtils.isLetterOrDigit(iptr.charAt(i))) continue;
if (iptr.charAt(i) == '_') continue;
iptr.setCharAt(i, '_');
}
return iptr.toString();
}
/**
* convert a string token into a valid EDIF string token (note - NOT re-entrant coding)
* In order to use NSC program ce2verilog, we need to suppress the '_' which replaces
* ']' in bus definitions.
*/
private String makeToken(String str)
{
if (str.length() == 0) return str;
StringBuffer sb = new StringBuffer();
if (TextUtils.isDigit(str.charAt(0))) sb.append('X');
for(int i=0; i<str.length(); i++)
{
char chr = str.charAt(i);
if (Character.isWhitespace(chr)) break;
if (chr == '[' || chr == '<') chr = '_';
if (TextUtils.isLetterOrDigit(chr) || chr == '&' || chr == '_')
sb.append(chr);
}
return sb.toString();
}
/****************************** GRAPHIC OUTPUT METHODS ******************************/
/**
* Method to output all graphic objects of a symbol.
*/
private void writeSymbol(Cell cell)
{
if (cell == null) return;
if (!cell.isIcon())
cell = cell.iconView();
if (cell == null) return;
blockOpen("symbol");
egraphic_override = EGWIRE;
egraphic = EGUNKNOWN;
for(Iterator<PortProto> it = cell.getPorts(); it.hasNext(); )
{
Export e = (Export)it.next();
writePortImplementation(e, true);
}
egraphic_override = EGUNKNOWN;
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
writeSymbolCell(ni, GenMath.MATID);
}
for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
writeSymbolArcInst(ai, GenMath.MATID);
}
// close figure
setGraphic(EGUNKNOWN);
blockClose("symbol");
}
private void writeSymbol(PrimitiveNode pn, NodeInst ni) {
if (pn == null) return;
blockOpen("symbol");
egraphic_override = EGWIRE;
egraphic = EGUNKNOWN;
for(Iterator<PortProto> it = pn.getPorts(); it.hasNext(); )
{
PortProto e = it.next();
blockOpen("portImplementation");
blockOpen("name");
blockPutIdentifier(makeToken(e.getName()));
blockOpen("display");
blockOpen("figureGroupOverride");
blockPutIdentifier(getFigureGroupName(EGWIRE));
blockOpen("textHeight");
blockPutInteger(getTextHeight(null));
blockClose("figureGroupOverride");
Poly portPoly = ni.getShapeOfPort(e);
//blockOpen("origin");
//writePoint(portPoly.getCenterX(), portPoly.getCenterY());
blockClose("name");
blockOpen("connectLocation");
writeSymbolPoly(portPoly, null, 1);
// close figure
setGraphic(EGUNKNOWN);
blockClose("portImplementation");
}
egraphic_override = EGUNKNOWN;
Poly [] polys = pn.getTechnology().getShapeOfNode(ni);
for (int i=0; i<polys.length; i++) {
writeSymbolPoly(polys[i], null, 1);
}
// close figure
setGraphic(EGUNKNOWN);
blockClose("symbol");
}
/**
* Write a portImplementation node.
* @param e
* @param closeBlock true to close block, false to leave portImplementation block open
*/
private void writePortImplementation(Export e, boolean closeBlock) {
blockOpen("portImplementation");
blockOpen("name");
blockPutIdentifier(makeToken(e.getName()));
blockOpen("display");
blockOpen("figureGroupOverride");
blockPutIdentifier(getFigureGroupName(EGWIRE));
blockOpen("textHeight");
blockPutInteger(getTextHeight(e.getTextDescriptor(Export.EXPORT_NAME)));
blockClose("figureGroupOverride");
blockOpen("origin");
Poly namePoly = e.getNamePoly();
writePoint(namePoly.getCenterX(), namePoly.getCenterY());
blockClose("name");
blockOpen("connectLocation");
Poly portPoly = e.getPoly();
egraphic_override = EGWIRE;
egraphic = EGUNKNOWN;
writeSymbolPoly(portPoly, null, 1);
setGraphic(EGUNKNOWN);
blockClose("connectLocation");
if (closeBlock) {
blockClose("portImplementation");
}
}
/**
* Method to output a specific symbol cell
*/
private void writeSymbolCell(NodeInst ni, AffineTransform prevtrans)
{
// make transformation matrix within the current nodeinst
if (ni.getOrient().equals(Orientation.IDENT))
{
writeSymbolNodeInst(ni, prevtrans);
} else
{
AffineTransform localtran = ni.rotateOut(prevtrans);
writeSymbolNodeInst(ni, localtran);
}
}
/**
* Method to symbol "ni" when transformed through "prevtrans".
*/
private void writeSymbolNodeInst(NodeInst ni, AffineTransform prevtrans)
{
NodeProto np = ni.getProto();
// primitive nodeinst: ask the technology how to draw it
if (!ni.isCellInstance())
{
Technology tech = np.getTechnology();
Poly [] polys = tech.getShapeOfNode(ni);
int high = polys.length;
// don't draw invisible pins
int low = 0;
if (np == Generic.tech().invisiblePinNode) low = 1;
for (int j = low; j < high; j++)
{
// get description of this layer
Poly poly = polys[j];
// draw the nodeinst
poly.transform(prevtrans);
// draw the nodeinst and restore the color
// check for text ...
boolean istext = false;
if (poly.getStyle().isText())
{
istext = true;
// close the current figure ...
setGraphic(EGUNKNOWN);
blockOpen("annotate");
}
writeSymbolPoly(poly, null, 1);
if (istext) blockClose("annotate");
}
} else
{
// transform into the nodeinst for display of its guts
Cell subCell = (Cell)np;
AffineTransform subrot = ni.translateOut(prevtrans);
// see if there are displayable variables on the cell
Poly[] varPolys = ni.getDisplayableVariables(ni.getBounds(), null, false, localPrefs.gp.isShowTempNames());
if (varPolys.length != 0)
setGraphic(EGUNKNOWN);
writeDisplayableVariables(varPolys, prevtrans);
// search through cell
for(Iterator<NodeInst> it = subCell.getNodes(); it.hasNext(); )
{
NodeInst sNi = it.next();
writeSymbolCell(sNi, subrot);
}
for(Iterator<ArcInst> it = subCell.getArcs(); it.hasNext(); )
{
ArcInst sAi = it.next();
writeSymbolArcInst(sAi, subrot);
}
}
}
/**
* Method to draw an arcinst. Returns indicator of what else needs to
* be drawn. Returns negative if display interrupted
*/
private void writeSymbolArcInst(ArcInst ai, AffineTransform trans)
{
// get the endpoints of the arcinst
Point2D [] points = new Point2D[2];
points[0] = new Point2D.Double(ai.getTailLocation().getX(), ai.getTailLocation().getY());
points[1] = new Point2D.Double(ai.getHeadLocation().getX(), ai.getHeadLocation().getY());
// translate point if needed
points[0] = equivs.translatePortConnection(points[0], ai.getTailPortInst());
points[1] = equivs.translatePortConnection(points[1], ai.getHeadPortInst());
Poly poly = new Poly(points);
poly.setStyle(Poly.Type.OPENED);
poly.transform(trans);
writeSymbolPoly(poly, null, 1);
// now get the variables
Poly [] varPolys = ai.getDisplayableVariables(ai.getBounds(), null, quiet, localPrefs.gp.isShowTempNames());
if (varPolys.length == 0)
setGraphic(EGUNKNOWN);
writeDisplayableVariables(varPolys, trans);
}
private void writeDisplayableVariables(Poly [] varPolys, AffineTransform prevtrans) {
for(int i=0; i<varPolys.length; i++)
{
Poly varPoly = varPolys[i];
String name = null;
String append = null;
double scale = 1;
DisplayedText dt = varPoly.getDisplayedText();
Variable var = null;
if (dt != null) var = dt.getVariable();
if (var != null)
{
// see if there is a translation
name = var.getTrueName();
EDIFEquiv.VariableEquivalence ve = equivs.getElectricVariableEquivalence(dt.getVariableKey().getName());
if (ve != null)
{
name = ve.externVarName;
append = ve.appendElecOutput;
scale = ve.scale;
}
}
if (name == null) continue;
if (prevtrans != null) varPoly.transform(prevtrans);
// make sure poly type is some kind of text
if (!varPoly.getStyle().isText() && var != null) {
TextDescriptor td = var.getTextDescriptor();
if (td != null) {
Poly.Type style = td.getPos().getPolyType();
varPoly.setStyle(style);
}
}
if (varPoly.getString() == null && var != null)
varPoly.setString(var.getObject().toString());
blockOpen("property");
blockPutIdentifier(name);
blockOpen("string");
writeSymbolPoly(varPoly, append, scale);
blockClose("property");
}
}
private void setGraphic(EGraphic type)
{
if (type == EGUNKNOWN)
{
// terminate the figure
if (egraphic != EGUNKNOWN) blockClose("figure");
egraphic = EGUNKNOWN;
} else if (egraphic_override == EGUNKNOWN)
{
// normal case
if (type != egraphic)
{
// new egraphic type
if (egraphic != EGUNKNOWN) blockClose("figure");
egraphic = type;
blockOpen("figure");
blockPutIdentifier(getFigureGroupName(egraphic));
}
} else if (egraphic != egraphic_override)
{
// override figure
if (egraphic != EGUNKNOWN) blockClose("figure");
egraphic = egraphic_override;
blockOpen("figure");
blockPutIdentifier(getFigureGroupName(egraphic));
}
}
/**
* Method to write polys into EDIF syntax
*/
private void writeSymbolPoly(Poly obj, String append, double scale)
{
// now draw the polygon
Poly.Type type = obj.getStyle();
Point2D [] points = obj.getPoints();
if (type == Poly.Type.CIRCLE || type == Poly.Type.DISC || type == Poly.Type.THICKCIRCLE)
{
setGraphic(EGART);
double i = points[0].distance(points[1]);
blockOpen("circle");
writePoint(points[0].getX() - i, points[0].getY());
writePoint(points[0].getX() + i, points[0].getY());
blockClose("circle");
return;
}
if (type == Poly.Type.CIRCLEARC || type == Poly.Type.THICKCIRCLEARC)
{
setGraphic(EGART);
// arcs at [i] points [1+i] [2+i] clockwise
if (points.length == 0) return;
if ((points.length % 3) != 0) return;
for (int i = 0; i < points.length; i += 3)
{
blockOpen("openShape");
blockOpen("curve");
blockOpen("arc");
writePoint(points[i + 1].getX(), points[i + 1].getY());
// calculate a point between the first and second point
Point2D si = GenMath.computeArcCenter(points[i], points[i + 1], points[i + 2]);
writePoint(si.getX(), si.getY());
writePoint(points[i + 2].getX(), points[i + 2].getY());
blockClose("openShape");
}
return;
}
if (type == Poly.Type.FILLED || type == Poly.Type.CLOSED)
{
Rectangle2D bounds = obj.getBox();
if (bounds != null)
{
// simple rectangular box
if (bounds.getWidth() == 0 && bounds.getHeight() == 0)
{
if (egraphic_override == EGUNKNOWN) return;
setGraphic(EGART);
blockOpen("dot");
writePoint(bounds.getCenterX(), bounds.getCenterY());
blockClose("dot");
} else
{
setGraphic(EGART);
blockOpen("rectangle");
writePoint(bounds.getMinX(), bounds.getMinY());
writePoint(bounds.getMaxY(), bounds.getMaxY());
blockClose("rectangle");
}
} else
{
setGraphic(EGART);
blockOpen("path");
blockOpen("pointList");
for (int i = 0; i < points.length; i++)
writePoint(points[i].getX(), points[i].getY());
if (points.length > 2) writePoint(points[0].getX(), points[0].getY());
blockClose("path");
}
return;
}
if (type.isText())
{
if (localPrefs.edifCadenceCompatibility && obj.getDisplayedText() != null) {
// Properties in Cadence do not have position info, Cadence
// determines position automatically and cannot be altered by user.
// There also does not seem to be any way to set the 'display' so
// that it shows up on the instance
String value = obj.getDisplayedText().getVariable().getPureValue(-1);
if (scale != 1)
{
double scaled = TextUtils.atof(value);
value = TextUtils.formatDouble(scaled * scale);
}
if (append != null) value += append;
String str = convertElectricPropToCadence(value);
str = str.replaceAll("\"", "%34%");
blockPutString(str);
return;
}
Rectangle2D bounds = obj.getBounds2D();
setGraphic(EGUNKNOWN);
blockOpen("stringDisplay");
String str = obj.getString().replaceAll("\"", "%34%");
if (append != null) str += append;
blockPutString(str);
blockOpen("display");
TextDescriptor td = obj.getTextDescriptor();
if (td != null)
{
blockOpen("figureGroupOverride");
blockPutIdentifier(getFigureGroupName(EGART));
// output the text height
blockOpen("textHeight");
// 2 pixels = 0.0278 in or 36 double pixels per inch
double height = getTextHeight(td);
blockPutInteger(height);
blockClose("figureGroupOverride");
} else {
blockPutIdentifier(EGART.getText());
}
if (type == Poly.Type.TEXTCENT) blockPut("justify", "CENTERCENTER"); else
if (type == Poly.Type.TEXTTOP) blockPut("justify", "LOWERCENTER"); else
if (type == Poly.Type.TEXTBOT) blockPut("justify", "UPPERCENTER"); else
if (type == Poly.Type.TEXTLEFT) blockPut("justify", "CENTERRIGHT"); else
if (type == Poly.Type.TEXTRIGHT) blockPut("justify", "CENTERLEFT"); else
if (type == Poly.Type.TEXTTOPLEFT) blockPut("justify", "LOWERRIGHT"); else
if (type == Poly.Type.TEXTBOTLEFT) blockPut("justify", "UPPERRIGHT"); else
if (type == Poly.Type.TEXTTOPRIGHT) blockPut("justify", "LOWERLEFT"); else
if (type == Poly.Type.TEXTBOTRIGHT) blockPut("justify", "UPPERLEFT");
blockPut("orientation", "R0");
blockOpen("origin");
writePoint(bounds.getMinX(), bounds.getMinY());
blockClose("stringDisplay");
return;
}
if (type == Poly.Type.OPENED || type == Poly.Type.OPENEDT1 ||
type == Poly.Type.OPENEDT2 || type == Poly.Type.OPENEDT3)
{
// check for closed 4 sided figure
if (points.length == 5 && points[4].getX() == points[0].getX() && points[4].getY() == points[0].getY())
{
Rectangle2D bounds = obj.getBox();
if (bounds != null)
{
// simple rectangular box
if (bounds.getWidth() == 0 && bounds.getHeight() == 0)
{
if (egraphic_override == EGUNKNOWN) return;
setGraphic(EGART);
blockOpen("dot");
writePoint(bounds.getCenterX(), bounds.getCenterY());
blockClose("dot");
} else
{
setGraphic(EGART);
blockOpen("rectangle");
writePoint(bounds.getMinX(), bounds.getMinY());
writePoint(bounds.getMaxX(), bounds.getMaxY());
blockClose("rectangle");
}
return;
}
}
setGraphic(EGART);
blockOpen("path");
blockOpen("pointList");
for (int i = 0; i < points.length; i++)
writePoint(points[i].getX(), points[i].getY());
blockClose("path");
return;
}
if (type == Poly.Type.VECTORS)
{
setGraphic(EGART);
for (int i = 0; i < points.length; i += 2)
{
blockOpen("path");
blockOpen("pointList");
writePoint(points[i].getX(), points[i].getY());
writePoint(points[i + 1].getX(), points[i + 1].getY());
blockClose("path");
}
return;
}
}
private String getFigureGroupName(EGraphic graphic)
{
String name = graphic.getText();
EDIFEquiv.FigureGroupEquivalence fge = equivs.getElectricFigureGroupEquivalence(name);
if (fge != null) name = fge.externFGName;
return name;
}
private void writeFigureGroup(EGraphic graphic) {
blockOpen("figureGroup");
blockPutIdentifier(getFigureGroupName(graphic));
blockClose("figureGroup");
}
private void writeScale(Technology tech) {
//double meters_to_lambda = TextUtils.convertDistance(1, tech, TextUtils.UnitScale.NONE);
blockOpen("scale");
blockPutInteger(160);
blockPutDouble(0.0254);
blockPut("unit", "DISTANCE");
blockClose("scale");
}
private double getTextHeight(TextDescriptor td) {
double size = 2; // default
if (td != null) {
size = td.getSize().getSize();
if (!td.getSize().isAbsolute()) {
size = size * 2;
}
}
// 2 pixels = 0.0278 in or 36 double pixels per inch
double height = size * 10 / 36;
return scaleValue(height);
}
private static final Pattern atPat = Pattern.compile("@(\\w+)");
private static final Pattern pPat = Pattern.compile("(P|PAR)\\(\"(\\w+)\"\\)");
/**
* Convert a property in Electric, with parameter passing, to
* Cadence parameter passing syntax
* @param prop the expression
* @return an equivalent expression in Cadence
*/
public static String convertElectricPropToCadence(String prop) {
StringBuffer sb = new StringBuffer();
Matcher atMat = atPat.matcher(prop);
while (atMat.find()) {
atMat.appendReplacement(sb, "P(\""+atMat.group(1)+"\")");
}
atMat.appendTail(sb);
prop = sb.toString();
sb = new StringBuffer();
Matcher pMat = pPat.matcher(prop);
while (pMat.find()) {
String c = "+"; // default
if (pMat.group(1).equals("PAR"))
c = "@";
pMat.appendReplacement(sb, "["+c+pMat.group(2)+"]");
}
pMat.appendTail(sb);
return sb.toString();
}
private static final Pattern pparPat = Pattern.compile("(pPar|iPar|atPar|dotPar|_Par)\\(\"(\\w+)\"\\)");
private static final Pattern bPat = Pattern.compile("\\[([~+.@])(\\w+)\\]");
/**
* Convert a property in Cadence, with parameter passing, to
* Electric parameter passing syntax
* @param prop the expression
* @return an equivalent expression in Electric
*/
public static String convertCadencePropToElectric(String prop) {
String origProp = prop;
StringBuffer sb = new StringBuffer();
Matcher atMat = bPat.matcher(prop);
while (atMat.find()) {
String call = "pPar";
if (atMat.group(1).equals("+")) call = "pPar";
else if (atMat.group(1).equals("@")) call = "atPar";
else {
System.out.println("Warning converting properties: Electric does not support \"["+atMat.group(1)+"param], using [+param] instead, in "+origProp);
}
//if (atMat.group(1).equals("~")) call = "iPar";
//if (atMat.group(1).equals(".")) call = "dotPar";
atMat.appendReplacement(sb, call+"(\""+atMat.group(2)+"\")");
}
atMat.appendTail(sb);
prop = sb.toString();
sb = new StringBuffer();
Matcher pMat = pparPat.matcher(prop);
while (pMat.find()) {
String c = "P"; // default
if (pMat.group(1).equals("pPar")) c = "P";
else if (pMat.group(1).equals("atPar")) c = "PAR";
else {
System.out.println("Warning converting properties: Electric does not support \"["+pMat.group(1)+"param], using pPar instead, in "+origProp);
}
pMat.appendReplacement(sb, c+"(\""+pMat.group(2)+"\")");
}
pMat.appendTail(sb);
return sb.toString();
}
/****************************** LOW-LEVEL BLOCK OUTPUT METHODS ******************************/
/**
* Will open a new keyword block, will indent the new block
* depending on depth of the keyword
*/
private void blockOpen(String keyword) {
blockOpen(keyword, true);
}
/**
* Will open a new keyword block, will indent the new block
* depending on depth of the keyword
*/
private void blockOpen(String keyword, boolean startOnNewLine)
{
if (blkstack_ptr > 0 && startOnNewLine) printWriter.print("\n");
// output the new block
blkstack[blkstack_ptr++] = keyword;
// output the keyword
String blanks = startOnNewLine ? getBlanks(blkstack_ptr-1) : " ";
printWriter.print(blanks + "( " + keyword);
}
/**
* Will output a one identifier block
*/
private void blockPut(String keyword, String identifier)
{
// output the new block
if (blkstack_ptr != 0) printWriter.print("\n");
// output the keyword
printWriter.print(getBlanks(blkstack_ptr) + "( " + keyword + " " + identifier + " )");
}
/**
* Will output a string identifier to the file
*/
private void blockPutIdentifier(String str)
{
printWriter.print(" " + str);
}
/**
* Will output a quoted string to the file
*/
private void blockPutString(String str)
{
printWriter.print(" \"" + str + "\"");
}
/**
* Will output an integer to the edif file
*/
private void blockPutInteger(double val)
{
printWriter.print(" " + TextUtils.formatDouble(val));
}
/**
* Will output a floating value to the edif file
*/
private static DecimalFormat decimalFormatScientific = null;
private void blockPutDouble(double val)
{
if (decimalFormatScientific == null)
{
decimalFormatScientific = new DecimalFormat("########E0");
decimalFormatScientific.setGroupingUsed(false);
}
String ret = decimalFormatScientific.format(val).replace('E', ' ');
ret = ret.replaceAll("\\.[0-9]+", "");
printWriter.print(" ( e " + ret + " )");
}
private void blockClose(String keyword)
{
if (blkstack_ptr == 0) return;
int depth = 1;
if (keyword != null)
{
// scan for this saved keyword
for (depth = 1; depth <= blkstack_ptr; depth++)
{
if (blkstack[blkstack_ptr - depth].equals(keyword)) break;
}
if (depth > blkstack_ptr)
{
reportError("EDIF output: could not match keyword <" + keyword + ">");
return;
}
}
// now terminate and free keyword list
do
{
blkstack_ptr--;
printWriter.print(" )");
} while ((--depth) > 0);
}
/**
* Method to terminate all currently open blocks.
*/
private void blockFinish()
{
if (blkstack_ptr > 0)
{
blockClose(blkstack[0]);
}
printWriter.print("\n");
}
private int blkstack_ptr;
private String [] blkstack = new String[50];
private String getBlanks(int num)
{
StringBuffer sb = new StringBuffer();
for(int i=0; i<num; i++) sb.append(' ');
return sb.toString();
}
/****************************** SUBCLASSED METHODS FOR THE TOPOLOGY ANALYZER ******************************/
/**
* Method to adjust a cell name to be safe for EDIF output.
* @param name the cell name.
* @return the name, adjusted for EDIF output.
*/
protected String getSafeCellName(String name) { return name; }
/** Method to return the proper name of Power */
protected String getPowerName(Network net) { return "VDD"; }
/** Method to return the proper name of Ground */
protected String getGroundName(Network net) { return "GND"; }
/** Method to return the proper name of a Global signal */
protected String getGlobalName(Global glob) { return glob.getName(); }
/** Method to report 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 always prepended to cell names. */
protected boolean isLibraryNameAlwaysAddedToCellName() { return false; }
/** Method to report that aggregate names (busses) are NOT used (bus information is extracted independently). */
protected boolean isAggregateNamesSupported() { return false; }
/** Abstract method to decide whether aggregate names (busses) can have gaps in their ranges. */
protected boolean isAggregateNameGapsSupported() { return false; }
/** Method to report whether input and output names are separated. */
protected boolean isSeparateInputAndOutput() { return true; }
/** Abstract method to decide whether netlister is case-sensitive (Verilog) or not (Spice). */
protected boolean isCaseSensitive() { return true; }
/**
* Method to adjust a network name to be safe for EDIF output.
*/
protected String getSafeNetName(String name, boolean bus) { return name; }
/** Tell the Hierarchy enumerator how to short resistors */
@Override
protected Netlist.ShortResistors getShortResistors() { return Netlist.ShortResistors.NO; }
/**
* Method to tell whether the topological analysis should mangle cell names that are parameterized.
*/
protected boolean canParameterizeNames() { return false; }
}