/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: FastHenry.java
* Input/output tool: FashHenry Netlist output
* Written 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.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.VarContext;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.technology.Layer;
import com.sun.electric.technology.Technology;
import com.sun.electric.tool.simulation.SimulationTool;
import com.sun.electric.util.TextUtils;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* This is the netlister for FastHenry.
*/
public class FastHenry extends Output
{
/** key of Variable holding group name. */ public static final Variable.Key GROUP_NAME_KEY = Variable.newKey("SIM_fasthenry_group_name");
/** key of Variable holding thickness. */ public static final Variable.Key THICKNESS_KEY = Variable.newKey("SIM_fasthenry_thickness");
/** key of Variable holding width subdivisions. */ public static final Variable.Key WIDTH_SUBDIVS_KEY = Variable.newKey("SIM_fasthenry_width_subdivs");
/** key of Variable holding height subdivisions. */ public static final Variable.Key HEIGHT_SUBDIVS_KEY = Variable.newKey("SIM_fasthenry_height_subdivs");
/** key of Variable holding the head Z value. */ public static final Variable.Key ZHEAD_KEY = Variable.newKey("SIM_fasthenry_z_head");
/** key of Variable holding the tail Z value. */ public static final Variable.Key ZTAIL_KEY = Variable.newKey("SIM_fasthenry_z_tail");
private FastHenryPreferences localPrefs;
/**
* Class for managing FastHenry information on arcs.
*/
public static class FastHenryArcInfo
{
private String groupName;
private double thickness;
private int widthSubdivisions, heightSubdivisions;
private double zHead, zTail;
private double zDefault;
public String getGroupName() { return groupName; }
public double getThickness() { return thickness; }
public int getWidthSubdivisions() { return widthSubdivisions; }
public int getHeightSubdivisions() { return heightSubdivisions; }
public double getZHead() { return zHead; }
public double getZTail() { return zTail; }
public double getZDefault() { return zDefault; }
public FastHenryArcInfo(ArcInst ai, FastHenryPreferences prefs)
{
Technology tech = ai.getProto().getTechnology();
// get the group membership
groupName = null;
Variable var = ai.getVar(GROUP_NAME_KEY);
if (var != null) groupName = var.getPureValue(-1);
// get the arc thickness
thickness = -1;
var = ai.getVar(THICKNESS_KEY);
if (var != null)
{
if (var.getObject() instanceof Integer) thickness = ((Integer)var.getObject()).intValue() / tech.getScale(); else
thickness = TextUtils.atof(var.getPureValue(-1));
}
// get the width subdivisions
widthSubdivisions = -1;
var = ai.getVar(WIDTH_SUBDIVS_KEY);
if (var != null) widthSubdivisions = TextUtils.atoi(var.getPureValue(-1));
// get the height subdivisions
heightSubdivisions = -1;
var = ai.getVar(HEIGHT_SUBDIVS_KEY);
if (var != null) heightSubdivisions = TextUtils.atoi(var.getPureValue(-1));
// get the Z height at the head of the arc
zHead = -1;
var = ai.getVar(ZHEAD_KEY);
if (var != null)
{
if (var.getObject() instanceof Integer) zHead = ((Integer)var.getObject()).intValue() / tech.getScale(); else
zHead = TextUtils.atof(var.getPureValue(-1));
}
// get the Z height at the tail of the arc
zTail = -1;
var = ai.getVar(ZTAIL_KEY);
if (var != null)
{
if (var.getObject() instanceof Integer) zTail = ((Integer)var.getObject()).intValue() / tech.getScale(); else
zTail = TextUtils.atof(var.getPureValue(-1));
}
// get the default Z height
zDefault = -1;
Poly [] polys = tech.getShapeOfArc(ai);
for(int i=0; i<polys.length; i++)
{
Poly poly = polys[i];
Layer layer = poly.getLayer();
if (layer == null) continue;
zDefault = layer.getDepth();
break;
}
}
}
public static class FastHenryPreferences extends OutputPreferences
{
int widthSubdivisions = SimulationTool.getFactoryFastHenryWidthSubdivisions();
int heightSubdivisions = SimulationTool.getFactoryFastHenryHeightSubdivisions();
double defThickness = SimulationTool.getFactoryFastHenryDefThickness();
boolean useSingleFrequency = SimulationTool.isFactoryFastHenryUseSingleFrequency();
double startFrequency = SimulationTool.getFactoryFastHenryStartFrequency();
double endFrequency = SimulationTool.getFactoryFastHenryEndFrequency();
int runsPerDecade = SimulationTool.getFactoryFastHenryRunsPerDecade();
public FastHenryPreferences(boolean factory) {
super(factory);
if (!factory)
fillPrefs();
}
private void fillPrefs()
{
widthSubdivisions = SimulationTool.getFastHenryWidthSubdivisions();
heightSubdivisions = SimulationTool.getFastHenryHeightSubdivisions();
defThickness = SimulationTool.getFastHenryDefThickness();
useSingleFrequency = SimulationTool.isFastHenryUseSingleFrequency();
startFrequency = SimulationTool.getFastHenryStartFrequency();
endFrequency = SimulationTool.getFastHenryEndFrequency();
runsPerDecade = SimulationTool.getFastHenryRunsPerDecade();
}
public Output doOutput(Cell cell, VarContext context, String filePath)
{
FastHenry out = new FastHenry(this);
if (out.openTextOutputStream(filePath)) return out.finishWrite();
out.writeFH(cell, context);
if (out.closeTextOutputStream()) return out.finishWrite();
System.out.println(filePath + " written");
return out.finishWrite();
}
}
/**
* Creates a new instance of the FastHenry netlister.
*/
FastHenry(FastHenryPreferences fp) { localPrefs = fp; }
protected void writeFH(Cell cell, VarContext context)
{
printWriter.println("* FastHenry for " + cell);
emitCopyright("* ", "");
if (localPrefs.includeDateAndVersionInOutput)
{
printWriter.println("* Cell created on " + TextUtils.formatDate(cell.getCreationDate()));
printWriter.println("* Cell last modified on " + TextUtils.formatDate(cell.getRevisionDate()));
printWriter.println("* Netlist written on " + TextUtils.formatDate(new Date()));
printWriter.println("* Written by Electric VLSI Design System, version " + Version.getVersion());
} else
{
printWriter.println("* Written by Electric VLSI Design System");
}
printWriter.println("\n* Units are microns");
printWriter.println(".units um");
// write default width and height subdivisions
printWriter.println("");
printWriter.println("* Default number of subdivisions");
printWriter.println(".Default nwinc=" + localPrefs.widthSubdivisions +
" nhinc=" + localPrefs.heightSubdivisions +
" h=" + TextUtils.formatDouble(localPrefs.defThickness));
// reset flags for cells that have been written
sim_writefhcell(cell);
// write frequency range
printWriter.println("");
if (!localPrefs.useSingleFrequency)
{
printWriter.println(".freq fmin=" + TextUtils.formatDouble(localPrefs.startFrequency) +
" fmax=" + TextUtils.formatDouble(localPrefs.endFrequency) +
" ndec=" + Integer.toString(localPrefs.runsPerDecade));
} else
{
printWriter.println(".freq fmin=" + TextUtils.formatDouble(localPrefs.startFrequency) +
" fmax=" + TextUtils.formatDouble(localPrefs.endFrequency) + " ndec=1");
}
// clean up
printWriter.println("");
printWriter.println(".end");
}
/**
* Method to print the FastHenry description of cell "cell".
*/
private void sim_writefhcell(Cell cell)
{
// look at every node in the cell
printWriter.println("");
printWriter.println("* Traces");
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
// see if this node has a FastHenry arc on it
boolean found = false;
double nodeZVal = 0;
for(Iterator<Connection> cIt = ni.getConnections(); cIt.hasNext(); )
{
Connection con = cIt.next();
ArcInst ai = con.getArc();
FastHenryArcInfo fhai = new FastHenryArcInfo(ai, localPrefs);
if (fhai.getGroupName() == null) continue;
double zVal = fhai.getZDefault();
if (con.getEndIndex() == ArcInst.HEADEND && fhai.getZHead() >= 0) zVal = fhai.getZHead();
if (con.getEndIndex() == ArcInst.TAILEND && fhai.getZTail() >= 0) zVal = fhai.getZTail();
if (found)
{
// "nodeZVal" used in proper order
if (zVal != nodeZVal)
reportWarning("Warning: inconsistent z value at " + ni);
}
nodeZVal = zVal;
found = true;
}
if (!found) continue;
// node is an end point: get its name
String nname = ni.getName();
if (ni.hasExports())
{
Export e = ni.getExports().next();
nname = e.getName();
}
// write the "N" line
double x = TextUtils.convertDistance(ni.getTrueCenterX(), cell.getTechnology(), TextUtils.UnitScale.MICRO);
double y = TextUtils.convertDistance(ni.getTrueCenterY(), cell.getTechnology(), TextUtils.UnitScale.MICRO);
double z = TextUtils.convertDistance(nodeZVal, cell.getTechnology(), TextUtils.UnitScale.MICRO);
printWriter.println("N_" + nname + " x=" + TextUtils.formatDouble(x) +
" y=" + TextUtils.formatDouble(y) + " z=" + TextUtils.formatDouble(z));
}
// look at every arc in the cell
for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
// get info about this arc, stop if not part of the FastHenry output
FastHenryArcInfo fhai = new FastHenryArcInfo(ai, localPrefs);
if (fhai.getGroupName() == null) continue;
// get size
double wid = ai.getLambdaBaseWidth();
// get the name of the nodes on each end
NodeInst n1 = ai.getHeadPortInst().getNodeInst();
String n1Name = n1.getName();
if (n1.hasExports())
n1Name = n1.getExports().next().getName();
NodeInst n2 = ai.getTailPortInst().getNodeInst();
String n2Name = n2.getName();
if (n2.hasExports())
n2Name = n2.getExports().next().getName();
// write the "E" line
double w = TextUtils.convertDistance(wid, cell.getTechnology(), TextUtils.UnitScale.MICRO);
StringBuffer sb = new StringBuffer();
sb.append("E_" + n1Name + "_" + n2Name + " N_" + n1Name + " N_" + n2Name + " w=" + TextUtils.formatDouble(w));
if (fhai.getThickness() > 0)
{
double h = TextUtils.convertDistance(fhai.getThickness(), cell.getTechnology(), TextUtils.UnitScale.MICRO);
sb.append(" h=" + TextUtils.formatDouble(h));
}
if (fhai.getWidthSubdivisions() > 0) sb.append(" nwinc=" + Integer.toString(fhai.getWidthSubdivisions()));
if (fhai.getHeightSubdivisions() > 0) sb.append(" nhinc=" + Integer.toString(fhai.getHeightSubdivisions()));
printWriter.println(sb.toString());
}
// find external connections
printWriter.println("");
printWriter.println("* External connections");
// look at every export in the cell
Set<ArcInst> arcsSeen = new HashSet<ArcInst>();
Set<Export> portsSeen = new HashSet<Export>();
for(Iterator<PortProto> it = cell.getPorts(); it.hasNext(); )
{
Export e = (Export)it.next();
if (portsSeen.contains(e)) continue;
portsSeen.add(e);
NodeInst ni = e.getOriginalPort().getNodeInst();
Connection con = null;
for(Iterator<Connection> cIt = ni.getConnections(); cIt.hasNext(); )
{
con = cIt.next();
if (con.getArc().getVar(GROUP_NAME_KEY) != null) break;
con = null;
}
if (con == null) continue;
// port "pp" is one end, now find the other
int thatEnd = 1 - con.getEndIndex();
Export oE = sim_fasthenryfindotherport(con.getArc(), thatEnd, arcsSeen);
if (oE == null)
{
reportWarning("Warning: trace on export " + e.getName() + " has no other end that is an export");
continue;
}
// found two ports: write the ".external" line
portsSeen.add(oE);
printWriter.println(".external N_" + e.getName() + " N_" + oE.getName());
}
// warn about arcs that aren't connected to ".external" lines
for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
if (arcsSeen.contains(ai)) continue;
if (ai.getVar(GROUP_NAME_KEY) == null) continue;
reportWarning("Warning: " + ai + " is not connected to an export");
}
}
private Export sim_fasthenryfindotherport(ArcInst ai, int end, Set<ArcInst> arcsSeen)
{
arcsSeen.add(ai);
NodeInst ni = ai.getPortInst(end).getNodeInst();
if (ni.hasExports()) return ni.getExports().next();
for(Iterator<Connection> it = ni.getConnections(); it.hasNext(); )
{
Connection con = it.next();
ArcInst oAi = con.getArc();
if (oAi == ai) continue;
Variable var = ai.getVar(GROUP_NAME_KEY);
if (var == null) continue;
int thatEnd = 1 - con.getEndIndex();
Export oE = sim_fasthenryfindotherport(oAi, thatEnd, arcsSeen);
if (oE != null) return oE;
}
return null;
}
}