/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: SpiceRCSimple.java
*
* Copyright (c) 2008 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.geometry.Poly;
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.hierarchy.View;
import com.sun.electric.database.network.Netlist;
import com.sun.electric.database.network.Network;
import com.sun.electric.database.text.TextUtils;
import com.sun.electric.database.topology.ArcInst;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.PortInst;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.Layer;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.Technology;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.JobException;
import com.sun.electric.tool.io.output.Topology.CellNetInfo;
import com.sun.electric.tool.io.output.Topology.CellSignal;
import com.sun.electric.tool.simulation.Simulation;
import com.sun.electric.tool.user.User;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* This is the simple-RC parasitics extractor for the Spice netlist writer.
*/
public class SpiceRCSimple extends SpiceParasiticsGeneral
{
SpiceRCSimple(Spice.SpicePreferences localPrefs)
{
super(localPrefs);
segmentedParasiticInfo = new ArrayList<SpiceSegmentedNets>();
}
/**
* Method to initialize cell being analyzed for RC parasitics.
* @param cell the Cell being analyzed.
* @param cni hierarchical traversal information for the Cell, including netlists and other connectivity data.
* @param layoutTechnology the Technology to use for the Cell (may be different
* from the Cell's actual Technology if the Cell is a schematic...this is the
* layout technology to use instead).
* @param exemptedNets as set of networks that should be exempted from the analysis
* @param info data from the hierarchy traverser that gives global network information.
* @return a SpiceSegmentedNets object for the Cell.
*/
public SpiceSegmentedNets initializeSegments(Cell cell, CellNetInfo cni, Technology layoutTechnology,
SpiceExemptedNets exemptedNets, Topology.MyCellInfo info)
{
// first create a set of segmentedNets for the Cell
boolean verboseSegmentNames = localPrefs.parasiticsUseVerboseNaming;
Simulation.SpiceParasitics spLevel = localPrefs.parasiticsLevel;
SpiceSegmentedNets segmentedNets = new SpiceSegmentedNets(cell, verboseSegmentNames, cni, localPrefs);
segmentedParasiticInfo.add(segmentedNets);
curSegmentedNets = segmentedNets;
// look at every arc in the Cell
Netlist netList = cni.getNetList();
double scale = layoutTechnology.getScale(); // scale to convert units to nanometers
Map<Network,Network> exemptedNetsFound = new HashMap<Network,Network>();
for (Iterator<ArcInst> ait = cell.getArcs(); ait.hasNext(); )
{
ArcInst ai = ait.next();
// see if the network is being extracted
boolean extractNet = true;
if (segmentedNets.isPowerGround(ai.getHeadPortInst()))
extractNet = false;
if (ai.getProto().getFunction() == ArcProto.Function.NONELEC)
extractNet = false;
Network net = netList.getNetwork(ai, 0);
double cap = 0;
double res = 0;
if (extractNet && localPrefs.parasiticsUseExemptedNetsFile)
{
// ignore nets in exempted nets file
if (localPrefs.parasiticsIgnoreExemptedNets)
{
// check if this net is exempted
if (exemptedNets.isExempted(info.getNetID(net)))
{
extractNet = false;
cap = 0;
if (!exemptedNetsFound.containsKey(net))
{
System.out.println("Not extracting net "+cell.describe(false)+" "+net.getName());
exemptedNetsFound.put(net, net);
cap = exemptedNets.getReplacementCap(cell, net);
}
}
// extract only nets in exempted nets file
} else
{
if (exemptedNets.isExempted(info.getNetID(net)))
{
if (!exemptedNetsFound.containsKey(net))
{
System.out.println("Extracting net "+cell.describe(false)+" "+net.getName());
exemptedNetsFound.put(net, net);
extractNet = true;
}
} else
{
extractNet = false;
}
}
}
if (!extractNet)
{
// don't need to short arcs on networks that aren't extracted, since it is
// guaranteed that both ends of the arc are named the same.
//segmentedNets.shortSegments(ai.getHeadPortInst(), ai.getTailPortInst());
continue;
}
// figure out res and cap, see if we should ignore it
double length = ai.getLambdaLength() * scale / 1000; // length in microns
double width = ai.getLambdaBaseWidth() * scale / 1000; // width in microns
double area = length * width;
double fringe = length*2;
Technology tech = ai.getProto().getTechnology();
Poly [] arcInstPolyList = tech.getShapeOfArc(ai);
int tot = arcInstPolyList.length;
for(int j=0; j<tot; j++)
{
Poly poly = arcInstPolyList[j];
if (poly.getStyle().isText()) continue;
if (poly.isPseudoLayer()) continue;
Layer layer = poly.getLayer();
if (layer.getTechnology() != layoutTechnology) continue;
if (!layer.isDiffusionLayer())
{
if (localPrefs.parasiticsExtractsC)
{
double areacap = area * layer.getCapacitance();
double fringecap = fringe * layer.getEdgeCapacitance();
cap = areacap + fringecap;
}
if (localPrefs.parasiticsExtractsR)
{
res = length/width * layer.getResistance();
}
}
}
int arcPImodels = SpiceSegmentedNets.getNumPISegments(res, layoutTechnology.getMaxSeriesResistance());
// add caps
segmentedNets.putSegment(ai.getHeadPortInst(), cap/(arcPImodels+1));
segmentedNets.putSegment(ai.getTailPortInst(), cap/(arcPImodels+1));
if (res <= cell.getTechnology().getMinResistance())
{
// short arc
segmentedNets.shortSegments(ai.getHeadPortInst(), ai.getTailPortInst());
} else
{
//System.out.println("Using resistance of "+res+" for arc "+ai.getName());
segmentedNets.addArcRes(ai, res);
if (arcPImodels > 1)
segmentedNets.addArcCap(ai, cap); // need to store cap later to break it up
}
segmentedNets.addExtractedNet(net);
}
// Don't take into account gate resistance: so we need to short two PortInsts
// of gate together if this is layout
for(Iterator<NodeInst> aIt = cell.getNodes(); aIt.hasNext(); )
{
NodeInst ni = aIt.next();
if (!ni.isCellInstance())
{
if (((PrimitiveNode)ni.getProto()).getGroupFunction() == PrimitiveNode.Function.TRANS)
{
// System.out.println("--Processing gate "+ni.getName());
PortInst gate0 = ni.getTransistorGatePort();
PortInst gate1 = ni.getTransistorAltGatePort();
Network gateNet0 = netList.getNetwork(gate0);
if ((gate0 != gate1) && segmentedNets.isExtractedNet(gateNet0))
{
//System.out.println("Shorting gate "+ni.getName()+" ports "+gate0.getPortProto().getName()+" and "+gate1.getPortProto().getName());
segmentedNets.shortSegments(gate0, gate1);
}
}
// merge wells
} else
{
// System.out.println("--Processing subcell "+ni.getName());
// short together pins if shorted by subcell
Cell subCell = (Cell)ni.getProto();
SpiceSegmentedNets subNets = getSegmentedNets(subCell);
// list of lists of shorted exports
if (subNets != null)
{
// subnets may be null if mixing schematics with layout technologies
for (Iterator<List<String>> it = subNets.getShortedExports(); it.hasNext(); )
{
List<String> exports = it.next();
PortInst pi1 = null;
// list of exports shorted together
for (String exportName : exports)
{
// get portinst on node
PortInst pi = ni.findPortInst(exportName);
if (pi1 == null)
{
pi1 = pi;
continue;
}
Network net = netList.getNetwork(pi);
if (segmentedNets.isExtractedNet(net))
segmentedNets.shortSegments(pi1, pi);
}
}
}
}
}
return segmentedNets;
}
/**
* Method to emit the proper subcircuit header for a signal.
* @param cs the signal to emit
* @param infstr the string buffer to fill with the emitted signal information.
*/
public void writeSubcircuitHeader(CellSignal cs, StringBuffer infstr)
{
Network net = cs.getNetwork();
Map<String,List<String>> shortedExportsMap = new HashMap<String,List<String>>();
// For a single logical network, we need to:
// 1) treat certain exports as separate so as not to short resistors on arcs between the exports
// 2) join certain exports that do not have resistors on arcs between them, and record this information
// so that the next level up knows to short networks connection to those exports.
for (Iterator<Export> it = net.getExports(); it.hasNext(); )
{
Export e = it.next();
PortInst pi = e.getOriginalPort();
String name = curSegmentedNets.getNetName(pi);
// exports are shorted if their segmented net names are the same (case (2))
List<String> shortedExports = shortedExportsMap.get(name);
if (shortedExports == null)
{
shortedExports = new ArrayList<String>();
shortedExportsMap.put(name, shortedExports);
// this is the first occurance of this segmented network, use the name as the export (1)
infstr.append(" " + name);
}
shortedExports.add(e.getName());
}
// record shorted exports
for (List<String> shortedExports : shortedExportsMap.values())
{
if (shortedExports.size() > 1)
curSegmentedNets.addShortedExports(shortedExports);
}
}
/**
* Method to emit the name of a signal on an instance call (the "X" statement).
* @param no the Nodable for the cell instance being examined.
* @param subNet the Network in the cell attached to that Nodable.
* @param subSegmentedNets the SpiceSegmentedNets object for the Nodable's Cell.
* @param infstr the string buffer in which to emit the name(s).
*/
public void getParasiticName(Nodable no, Network subNet, SpiceSegmentedNets subSegmentedNets, StringBuffer infstr)
{
// connect to all exports (except power and ground of subcell net)
List<String> exportNames = new ArrayList<String>();
for (Iterator<Export> it = subNet.getExports(); it.hasNext(); )
{
// get subcell export, unless collapsed due to less than min R
Export e = it.next();
PortInst pi = e.getOriginalPort();
String name = subSegmentedNets.getNetName(pi);
if (exportNames.contains(name)) continue;
exportNames.add(name);
// ok, there is a port on the subckt on this subcell for this export,
// now get the appropriate network in this cell
pi = no.getNodeInst().findPortInstFromProto(no.getProto().findPortProto(e.getNameKey()));
name = curSegmentedNets.getNetName(pi);
infstr.append(" " + name);
}
}
/**
* Method to find the SpiceSegmentedNets object that corresponds to a given Cell.
* @param cell the Cell to find.
* @return the SpiceSegmentedNets object associated with that cell (null if none found).
*/
public SpiceSegmentedNets getSegmentedNets(Cell cell)
{
for (SpiceSegmentedNets seg : segmentedParasiticInfo)
if (seg.getCell() == cell) return seg;
return null;
}
/**
* Method called at the end of netlist writing to deal with back-annotation.
*/
public void backAnnotate()
{
Set<Cell> cellsToClear = new HashSet<Cell>();
List<PortInst> capsOnPorts = new ArrayList<PortInst>();
List<String> valsOnPorts = new ArrayList<String>();
List<ArcInst> resOnArcs = new ArrayList<ArcInst>();
List<Double> valsOnArcs = new ArrayList<Double>();
for (SpiceSegmentedNets segmentedNets : segmentedParasiticInfo)
{
Cell cell = segmentedNets.getCell();
if (cell.getView() != View.LAYOUT) continue;
// gather cells to clear capacitor values
cellsToClear.add(cell);
// gather capacitor updates
for (SpiceSegmentedNets.NetInfo info : segmentedNets.getUniqueSegments())
{
PortInst pi = info.getPortIterator().next();
if (info.getCap() > cell.getTechnology().getMinCapacitance())
{
capsOnPorts.add(pi);
valsOnPorts.add(TextUtils.formatDouble(info.getCap(), 2) + "fF");
}
}
// gather resistor updates
for (Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
Double res = segmentedNets.getRes(ai);
resOnArcs.add(ai);
valsOnArcs.add(res);
}
}
new BackAnnotateJob(cellsToClear, capsOnPorts, valsOnPorts, resOnArcs, valsOnArcs);
}
/**
* Class to run back-annotation in a Job.
*/
private static class BackAnnotateJob extends Job
{
private Set<Cell> cellsToClear;
private List<PortInst> capsOnPorts;
private List<String> valsOnPorts;
private List<ArcInst> resOnArcs;
private List<Double> valsOnArcs;
private BackAnnotateJob(Set<Cell> cellsToClear, List<PortInst> capsOnPorts, List<String> valsOnPorts,
List<ArcInst> resOnArcs, List<Double> valsOnArcs)
{
super("Spice Layout Back Annotate", User.getUserTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
this.capsOnPorts = capsOnPorts;
this.valsOnPorts = valsOnPorts;
this.resOnArcs = resOnArcs;
this.valsOnArcs = valsOnArcs;
this.cellsToClear = cellsToClear;
startJob();
}
public boolean doIt() throws JobException
{
TextDescriptor ctd = TextDescriptor.getPortInstTextDescriptor().withDispPart(TextDescriptor.DispPos.NAMEVALUE);
TextDescriptor rtd = TextDescriptor.getArcTextDescriptor().withDispPart(TextDescriptor.DispPos.NAMEVALUE);
int capCount = 0;
int resCount = 0;
// clear caps on layout
for(Cell cell : cellsToClear)
{
// delete all C's already on layout
for (Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
for (Iterator<PortInst> pit = ni.getPortInsts(); pit.hasNext(); )
{
PortInst pi = pit.next();
Variable var = pi.getVar(ATTR_C);
if (var != null) pi.delVar(var.getKey());
}
}
}
// add new C's
for(int i=0; i<capsOnPorts.size(); i++)
{
PortInst pi = capsOnPorts.get(i);
String str = valsOnPorts.get(i);
pi.newVar(ATTR_C, str, ctd);
resCount++;
}
// add new R's
for(int i=0; i<resOnArcs.size(); i++)
{
ArcInst ai = resOnArcs.get(i);
Double res = valsOnArcs.get(i);
// delete R if no new one
Variable var = ai.getVar(ATTR_R);
if (res == null && var != null)
ai.delVar(ATTR_R);
// change R if new one
if (res != null)
{
ai.newVar(ATTR_R, res, rtd);
resCount++;
}
}
System.out.println("Back-annotated "+resCount+" Resistors and "+capCount+" Capacitors");
return true;
}
}
}