/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: Schematic.java
*
* Copyright (c) 2004 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.drc;
import com.sun.electric.database.geometry.EPoint;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.Export;
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.network.NetworkTool;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.text.Name;
import com.sun.electric.database.text.TextUtils;
import com.sun.electric.database.topology.ArcInst;
import com.sun.electric.database.topology.Connection;
import com.sun.electric.database.topology.Geometric;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.PortInst;
import com.sun.electric.database.topology.RTBounds;
import com.sun.electric.database.variable.ElectricObject;
import com.sun.electric.database.variable.Variable;
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.user.ErrorLogger;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
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;
/**
* Class to do schematic design-rule checking.
* Examines artwork of a schematic for sensibility.
*/
public class Schematic
{
// Cells, nodes and arcs
private Set<ElectricObject> nodesChecked = new HashSet<ElectricObject>();
private ErrorLogger errorLogger;
private Map<Geometric,List<Variable>> newVariables = new HashMap<Geometric,List<Variable>>();
public static void doCheck(ErrorLogger errorLog, Cell cell, Geometric[] geomsToCheck, DRC.DRCPreferences dp)
{
Schematic s = new Schematic();
s.errorLogger = errorLog;
s.checkSchematicCellRecursively(cell, geomsToCheck);
DRC.addDRCUpdate(0, null, null, null, null, s.newVariables, dp);
}
private Cell isACellToCheck(Geometric geo)
{
if (geo instanceof NodeInst)
{
NodeInst ni = (NodeInst)geo;
// ignore documentation icon
if (ni.isIconOfParent()) return null;
if (!ni.isCellInstance()) return null;
Cell subCell = (Cell)ni.getProto();
Cell contentsCell = subCell.contentsView();
if (contentsCell == null) contentsCell = subCell;
if (nodesChecked.contains(contentsCell)) return null;
return contentsCell;
}
return null;
}
private void checkSchematicCellRecursively(Cell cell, Geometric[] geomsToCheck)
{
nodesChecked.add(cell);
// ignore if not a schematic
if (!cell.isSchematic() && cell.getTechnology() != Schematics.tech())
return;
// recursively check contents in case of hierchically checking
if (geomsToCheck == null)
{
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
Cell contentsCell = isACellToCheck(ni);
if (contentsCell != null)
checkSchematicCellRecursively(contentsCell, geomsToCheck);
}
}
else
{
for (Geometric geo : geomsToCheck)
{
Cell contentsCell = isACellToCheck(geo);
if (contentsCell != null)
checkSchematicCellRecursively(contentsCell, geomsToCheck);
}
}
// now check this cell
System.out.println("Checking schematic " + cell);
ErrorGrouper eg = new ErrorGrouper(cell);
checkSchematicCell(cell, false, geomsToCheck, eg);
}
private int cellIndexCounter;
private class ErrorGrouper
{
private boolean inited;
private int cellIndex;
private Cell cell;
ErrorGrouper(Cell cell)
{
inited = false;
cellIndex = cellIndexCounter++;
this.cell = cell;
}
public int getSortKey()
{
if (!inited)
{
inited = true;
errorLogger.setGroupName(cellIndex, cell.getName());
}
return cellIndex;
}
}
private void checkSchematicCell(Cell cell, boolean justThis, Geometric[] geomsToCheck, ErrorGrouper eg)
{
int initialErrorCount = errorLogger.getNumErrors();
Netlist netlist = cell.getNetlist();
// Normal hierarchically geometry
if (geomsToCheck == null)
{
for(Iterator<NodeInst> it = cell.getNodes(); it.hasNext(); )
{
NodeInst ni = it.next();
if (!ni.isCellInstance() &&
ni.getProto().getTechnology() == Generic.tech()) continue;
schematicDoCheck(netlist, ni, eg);
}
for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); )
{
ArcInst ai = it.next();
schematicDoCheck(netlist, ai, eg);
}
} else
{
for (Geometric geo : geomsToCheck)
schematicDoCheck(netlist, geo, eg);
}
checkCaseInsensitiveNetworks(netlist, eg);
int errorCount = errorLogger.getNumErrors();
int thisErrors = errorCount - initialErrorCount;
String indent = " ";
if (justThis) indent = "";
if (thisErrors == 0) System.out.println(indent + "No errors found"); else
System.out.println(indent + thisErrors + " errors found");
if (justThis) errorLogger.termLogging(true);
}
/**
* Method to add all variables of a given NodeInst that must be added after Schematics DRC job is done.
*/
private void addVariable(NodeInst ni, Variable var)
{
List<Variable> list = newVariables.get(ni);
if (list == null) // first time
{
list = new ArrayList<Variable>();
newVariables.put(ni, list);
}
list.add(var);
}
/**
* Method to check schematic object "geom".
*/
private void schematicDoCheck(Netlist netlist, Geometric geom, ErrorGrouper eg)
{
// Checked already
if (nodesChecked.contains(geom))
return;
nodesChecked.add(geom);
Cell cell = geom.getParent();
if (geom instanceof NodeInst)
{
NodeInst ni = (NodeInst)geom;
NodeProto np = ni.getProto();
// check for bus pins that don't connect to any bus arcs
if (np == Schematics.tech().busPinNode)
{
// proceed only if it has no exports on it
if (!ni.hasExports())
{
// must not connect to any bus arcs
boolean found = false;
for(Iterator<Connection> it = ni.getConnections(); it.hasNext(); )
{
Connection con = it.next();
if (con.getArc().getProto() == Schematics.tech().bus_arc) { found = true; break; }
}
if (!found)
{
errorLogger.logError("Bus pin does not connect to any bus arcs", geom, cell, null, eg.getSortKey());
return;
}
}
// flag bus pin if more than 1 wire is connected
int i = 0;
for(Iterator<Connection> it = ni.getConnections(); it.hasNext(); )
{
Connection con = it.next();
if (con.getArc().getProto() == Schematics.tech().wire_arc) i++;
}
if (i > 1)
{
List<Geometric> geomList = new ArrayList<Geometric>();
geomList.add(geom);
for(Iterator<Connection> it = ni.getConnections(); it.hasNext(); )
{
Connection con = it.next();
if (con.getArc().getProto() == Schematics.tech().wire_arc) i++;
geomList.add(con.getArc());
}
errorLogger.logMessage("Wire arcs cannot connect through a bus pin", geomList, cell, eg.getSortKey(), true);
return;
}
}
// check all pins
if (np.getFunction().isPin())
{
// may be stranded if there are no exports or arcs
if (!ni.hasExports() && !ni.hasConnections())
{
// see if the pin has displayable variables on it
boolean found = false;
for(Iterator<Variable> it = ni.getVariables(); it.hasNext(); )
{
Variable var = it.next();
if (var.isDisplay()) { found = true; break; }
}
if (!found)
{
errorLogger.logError("Stranded pin (not connected or exported)", geom, cell, null, eg.getSortKey());
return;
}
}
if (ni.isInlinePin())
{
errorLogger.logError("Unnecessary pin (between 2 arcs)", geom, cell, null, eg.getSortKey());
return;
}
Point2D pinLoc = ni.invisiblePinWithOffsetText(false);
if (pinLoc != null)
{
List<Geometric> geomList = new ArrayList<Geometric>();
List<EPoint> ptList = new ArrayList<EPoint>();
geomList.add(geom);
ptList.add(new EPoint(ni.getAnchorCenterX(), ni.getAnchorCenterY()));
ptList.add(new EPoint(pinLoc.getX(), pinLoc.getY()));
errorLogger.logMessageWithLines("Invisible pin has text in different location",
geomList, ptList, cell, eg.getSortKey(), true);
return;
}
}
// check parameters
if (np instanceof Cell)
{
Cell instCell = (Cell)np;
Cell contentsCell = instCell.contentsView();
if (contentsCell == null) contentsCell = instCell;
// ensure that this node matches the parameter list
for(Iterator<Variable> it = ni.getDefinedParameters(); it.hasNext(); )
{
Variable var = it.next();
assert ni.isParam(var.getKey());
Variable foundVar = contentsCell.getParameter(var.getKey());
if (foundVar == null)
{
// this node's parameter is no longer on the cell: delete from instance
String trueVarName = var.getTrueName();
errorLogger.logError("Parameter '" + trueVarName + "' on " + ni +
" is invalid", geom, cell, null, eg.getSortKey());
} else
{
// this node's parameter is still on the cell: make sure units are OK
if (var.getUnit() != foundVar.getUnit())
{
String trueVarName = var.getTrueName();
errorLogger.logError("Parameter '" + trueVarName + "' on " + ni +
" had incorrect units (now fixed)", geom, cell, null, eg.getSortKey());
addVariable(ni, var.withUnit(foundVar.getUnit()));
}
// make sure visibility is OK
if (foundVar.isInterior())
{
if (var.isDisplay())
{
String trueVarName = var.getTrueName();
errorLogger.logError("Parameter '" + trueVarName + "' on " + ni +
" should not be visible (now fixed)", geom, cell, null, eg.getSortKey());
addVariable(ni, var.withDisplay(false));
}
} else
{
if (!var.isDisplay())
{
String trueVarName = var.getTrueName();
errorLogger.logError("Parameter '" + trueVarName + "' on " + ni +
" should be visible (now fixed)", geom, cell, null, eg.getSortKey());
addVariable(ni, var.withDisplay(true));
}
}
}
}
// make sure instance name isn't the same as a network in the cell
String nodeName = ni.getName();
for(Iterator<Network> it = netlist.getNetworks(); it.hasNext(); )
{
Network net = it.next();
if (net.hasName(nodeName))
{
errorLogger.logError("Node " + ni + " is named '" + nodeName +
"' which conflicts with a network name in this cell", geom, cell, null, eg.getSortKey());
break;
}
}
}
// check all exports for proper icon/schematics characteristics match
Cell parentCell = ni.getParent();
for(Iterator<Cell> cIt = parentCell.getCellGroup().getCells(); cIt.hasNext(); )
{
Cell iconCell = cIt.next();
if (iconCell.getView() != View.ICON) continue;
for(Iterator<Export> it = ni.getExports(); it.hasNext(); )
{
Export e = it.next();
Export iconExport = e.getEquivalentPort(iconCell);
if (iconExport == null) continue;
if (e.getCharacteristic() != iconExport.getCharacteristic())
{
errorLogger.logError("Export '" + e.getName() + "' on " + ni +
" is " + e.getCharacteristic().getFullName() +
" but export in icon cell " + iconCell.describe(false) + " is " +
iconExport.getCharacteristic().getFullName(), geom, cell, null, eg.getSortKey());
}
}
}
// check for port overlap
checkPortOverlap(netlist, ni, eg);
} else
{
ArcInst ai = (ArcInst)geom;
// check for being floating if it does not have a visible name on it
boolean checkDangle = false;
if (Artwork.isArtworkArc(ai.getProto()))
return; // ignore artwork arcs
Name arcName = ai.getNameKey();
if (arcName == null || arcName.isTempname()) checkDangle = true;
if (checkDangle)
{
// do not check for dangle when busses are on named networks
if (ai.getProto() == Schematics.tech().bus_arc)
{
Name name = netlist.getBusName(ai);
if (name != null && !name.isTempname()) checkDangle = false;
}
}
if (checkDangle)
{
// check to see if this arc is floating
for(int i=0; i<2; i++)
{
NodeInst ni = ai.getPortInst(i).getNodeInst();
// OK if not a pin
if (!ni.getProto().getFunction().isPin()) continue;
// OK if it has exports on it
if (ni.hasExports()) continue;
// OK if it connects to more than 1 arc
if (ni.getNumConnections() != 1) continue;
// the arc dangles
errorLogger.logError("Arc dangles", geom, cell, null, eg.getSortKey());
return;
}
}
// check to see if its width is sensible
int signals = netlist.getBusWidth(ai);
if (signals < 1) signals = 1;
for(int i=0; i<2; i++)
{
PortInst pi = ai.getPortInst(i);
NodeInst ni = pi.getNodeInst();
if (!ni.isCellInstance()) continue;
Cell subNp = (Cell)ni.getProto();
PortProto pp = pi.getPortProto();
Cell np = subNp.contentsView();
if (np != null)
{
pp = ((Export)pi.getPortProto()).getEquivalent();
if (pp == null || pp == pi.getPortProto())
{
List<Geometric> geomList = new ArrayList<Geometric>();
geomList.add(geom);
geomList.add(ni);
errorLogger.logMessage("Arc " + ai.describe(true) + " connects to " +
pi.getPortProto() + " of " + ni + ", but there is no equivalent port in " + np,
geomList, cell, eg.getSortKey(), true);
continue;
}
}
int portWidth = netlist.getBusWidth((Export)pp);
if (portWidth < 1) portWidth = 1;
int nodeSize = ni.getNameKey().busWidth();
if (nodeSize <= 0) nodeSize = 1;
if (signals != portWidth && signals != portWidth*nodeSize)
{
List<Geometric> geomList = new ArrayList<Geometric>();
geomList.add(geom);
geomList.add(ni);
errorLogger.logMessage("Arc " + ai.describe(true) + " (" + signals + " wide) connects to " +
pp + " of " + ni + " (" + portWidth + " wide)", geomList, cell, eg.getSortKey(), true);
}
}
}
}
/**
* Method to check whether any port on a node overlaps others without connecting.
*/
private void checkPortOverlap(Netlist netlist, NodeInst ni, ErrorGrouper eg)
{
if (ni.getProto().getTechnology() == Generic.tech() ||
ni.getProto().getTechnology() == Artwork.tech()) return;
Cell cell = ni.getParent();
for(Iterator<PortInst> it = ni.getPortInsts(); it.hasNext(); )
{
PortInst pi = it.next();
Network net = netlist.getNetwork(pi);
Rectangle2D bounds = pi.getPoly().getBounds2D();
for(Iterator<RTBounds> sIt = cell.searchIterator(bounds); sIt.hasNext(); )
{
Geometric oGeom = (Geometric)sIt.next();
if (!(oGeom instanceof NodeInst)) continue;
NodeInst oNi = (NodeInst)oGeom;
if (ni == oNi) continue;
if (ni.getNodeIndex() > oNi.getNodeIndex()) continue;
if (oNi.getProto().getTechnology() == Generic.tech() ||
oNi.getProto().getTechnology() == Artwork.tech()) continue;
// see if ports touch
for(Iterator<PortInst> pIt = oNi.getPortInsts(); pIt.hasNext(); )
{
PortInst oPi = pIt.next();
Rectangle2D oBounds = oPi.getPoly().getBounds2D();
if (bounds.getMaxX() < oBounds.getMinX()) continue;
if (bounds.getMinX() > oBounds.getMaxX()) continue;
if (bounds.getMaxY() < oBounds.getMinY()) continue;
if (bounds.getMinY() > oBounds.getMaxY()) continue;
// see if they are connected
if (net == netlist.getNetwork(oPi)) continue;
// report the error
List<Geometric> geomList = new ArrayList<Geometric>();
geomList.add(ni);
geomList.add(oNi);
errorLogger.logMessage("Nodes '" + ni + "' '" + oNi + "' have touching ports that are not connected",
geomList, cell, eg.getSortKey(), true);
return;
}
}
}
}
private void checkCaseInsensitiveNetworks(Netlist netlist, ErrorGrouper eg) {
Cell cell = netlist.getCell();
HashMap<String, Network> canonicToNetwork = new HashMap<String, Network>();
for (Iterator<Network> it = netlist.getNetworks(); it.hasNext(); ) {
Network net = it.next();
for (Iterator<String> sit = net.getNames(); sit.hasNext(); ) {
String s = sit.next();
String cs = TextUtils.canonicString(s);
Network net1 = canonicToNetwork.get(cs);
if (net1 == null ) {
canonicToNetwork.put(cs, net);
} else if (net1 != net) {
String message = "Network: Schematic " + cell.libDescribe() + " doesn't connect " + net + " and " + net1;
boolean sameName = net1.hasName(s);
if (sameName)
message += " Like-named Global and Export may be connected in future releases";
System.out.println(message);
List<Geometric> geomList = new ArrayList<Geometric>();
push(geomList, net);
push(geomList, net1);
errorLogger.logMessage(message, geomList, cell, eg.getSortKey(), sameName);
}
}
}
}
private void push(List<Geometric> geomList, Network net) {
Iterator<Export> eit = net.getExports();
if (eit.hasNext()) {
geomList.add(eit.next().getOriginalPort().getNodeInst());
return;
}
Iterator<ArcInst> ait = net.getArcs();
if (ait.hasNext()) {
geomList.add(ait.next());
return;
}
Iterator<PortInst> pit = net.getPorts();
if (pit.hasNext()) {
geomList.add(pit.next().getNodeInst());
return;
}
}
}