/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: CalibreDrcErrors.java
*
* Copyright (c) 2005 Sun Microsystems and Static Free Software
*
* Electric(tm) is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Electric(tm) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Electric(tm); see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, Mass 02111-1307, USA.
*/
package com.sun.electric.tool.drc;
import com.sun.electric.database.geometry.EPoint;
import com.sun.electric.database.geometry.PolyBase;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.variable.UserInterface;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.user.ErrorLogger;
import java.awt.Shape;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
/**
* This reads an ASCII Calibre DRC error database
* produced by running Calibre DRC.
* It shows these errors on the specified cells in
* Electric.
*/
public class CalibreDrcErrors {
private int scale;
private String topCellName;
private Cell topCell;
private ErrorLogger logger;
private BufferedReader in;
private List<DrcRuleViolation> ruleViolations; // list of DrcRuleViolations
private int lineno;
private Map<Cell,String> mangledNames;
private String type;
private String filename;
private boolean noPopupMessages;
private static final String spaces = "[\\s\\t ]+";
/**
* Create a new CalibreDrcError class, read the errors in,
* and then convert them to the ErrorLogger.
* @param filename the ASCII calibre drc results database file
* @return number of erros. Negative number in case of valid data.
*/
public static int importErrors(String filename, Map<Cell,String> mangledNames, String type, boolean noPopupMessages) {
BufferedReader in;
try {
FileReader reader = new FileReader(filename);
in = new BufferedReader(reader);
} catch (IOException e) {
System.out.println("Error importing "+type+" Errors: "+e.getMessage());
return -1;
}
if (in == null) return -1;
CalibreDrcErrors errors = new CalibreDrcErrors(in, mangledNames, type, noPopupMessages);
errors.filename = filename;
// read first line
if (!errors.readTop()) return -1;
// read all rule violations
if (!errors.readRules()) return -1;
// finish
return errors.done();
}
// Constructor
private CalibreDrcErrors(BufferedReader in, Map<Cell,String> mangledNames, String type, boolean noPopupMessages) {
assert(in != null);
this.in = in;
lineno = 0;
ruleViolations = new ArrayList<DrcRuleViolation>();
this.mangledNames = mangledNames;
this.type = type;
this.noPopupMessages = noPopupMessages;
}
// read the cell name and precision, if any
private boolean readTop() {
scale = 1000;
String line;
try {
line = readLine(true);
} catch (IOException e) {
System.out.println("Error reading first line of file: "+e.getMessage());
return false;
}
if (line == null) return false;
String [] parts = line.trim().split(spaces);
if (parts.length == 1) {
topCellName = parts[0];
} else if (parts.length == 2) {
topCellName = parts[0];
try {
scale = Integer.parseInt(parts[1]);
} catch (NumberFormatException e) {
System.out.println("Error converting precision '"+parts[1]+"' to a number, using default of "+scale);
return false;
}
} else {
// error
System.out.println("Error on first line: Expected cell name and precision, or 'drc'");
return false;
}
topCell = getCell(topCellName, mangledNames);
if (topCell == null) {
System.out.println("Cannot find cell "+topCellName+" specified in error file, line number "+lineno+", aborting");
return false;
}
return true;
}
// read all Rule violations in the file
private boolean readRules() {
// read all errors
try {
while(true) {
DrcRuleViolation v = readRule();
if (v == null) break;
if (v.errors.size() != 0) // something to include
ruleViolations.add(v);
}
} catch (IOException e) {
System.out.println("Error reading file: "+e.getMessage());
return false;
}
return true;
}
/**
* Read a logged error. Return false if there was a problem, or nothing left to read.
* @return true if read ok, false if error or End of File
*/
private DrcRuleViolation readRule() throws IOException {
// read the first line: the rule name
String ruleName = readLine(false);
if (ruleName == null) return null; // EOF, no more errors
// read the header start line, tells us how many header lines there are
Header header = readHeader();
if (header == null) return null;
// read the rest of the header
for (int i=0; i<header.headerLength; i++) {
String s = readLine();
if (s == null) return null;
header.addHeaderLine(s);
}
if (header.comment.length() == 0) header.comment.append(ruleName);
DrcRuleViolation v = new DrcRuleViolation(ruleName, header);
// read shapes describing errors
incell = topCell;
for (int i=0; i<header.currentDrcResultsCount; i++) {
DrcError drc = readErrorShape();
if (drc == null) break;
v.addError(drc);
}
return v;
}
private Cell incell = null;
// read the header of the rule violation
private Header readHeader() throws IOException {
String headerStart = readLine();
if (headerStart == null) return null;
StringTokenizer tokenizer = new StringTokenizer(headerStart);
Header header = null;
try {
String cur = tokenizer.nextToken();
String orig = tokenizer.nextToken();
String len = tokenizer.nextToken();
int icur = Integer.parseInt(cur);
int iorig = Integer.parseInt(orig);
int ilen = Integer.parseInt(len);
header = new Header(icur, iorig, ilen);
} catch (NoSuchElementException e) {
System.out.println("Error parsing header start line, expected three integers on line number "+lineno+": "+headerStart);
return null;
} catch (NumberFormatException e) {
System.out.println("Error converting count strings to integers on header start line, line number "+lineno+": "+headerStart);
return null;
}
return header;
}
// populate a list of error shapes. return false on error.
private DrcError readErrorShape() throws IOException {
// we need to peek ahead, as it is unspecified how many error shapes there
// may be
String nextLine = readLine().trim();
boolean boole = nextLine.startsWith("e") ? true : false;
boolean boolp = nextLine.startsWith("p") ? true : false;
if (boole || boolp) {
String [] parts = nextLine.split(spaces);
if (parts.length != 3) {
System.out.println("Error on shape: expected ordinal and count numbers, line number "+lineno+": "+nextLine);
return null;
}
// int ordinal = 0;
int lines = 0;
try {
// ordinal = Integer.parseInt(parts[1]);
lines = Integer.parseInt(parts[2]);
} catch (NumberFormatException e) {
System.out.println("Error on shape: expected ordinal and count numbers, line number "+lineno+": "+nextLine);
return null;
}
// need to peek ahead to see if next line specifies a subcell
nextLine = readLine().trim();
if (nextLine.startsWith("CN")) {
parts = nextLine.split(spaces);
if (parts.length < 3) {
System.out.println("Error reading CN line, expected at least three fields, line number "+lineno+": "+nextLine);
return null;
}
String cellname = parts[1];
String coordSpace = parts[2];
if (coordSpace.equals("c")) {
// coords are in sub cell coord system
incell = getCell(cellname, mangledNames);
if (incell == null) incell = topCell;
}
nextLine = readLine();
}
DrcError drc = new DrcError(incell);
double lambdaScale = incell.getTechnology().getScale() / 1000;
// parse list of edges if this is edges shape
if (boole) {
for (int i=0; i<lines; i++) {
if (i != 0) // skip first line read, done already when we looked for CN
nextLine = readLine();
Shape s = parseErrorEdge(nextLine, lambdaScale);
if (s == null) return drc;
drc.addShape(s);
}
}
// parse list of poly vertices if this is a poly
else {
// boolp
Point2D [] points = new Point2D[lines];
for (int i=0; i<lines; i++) {
if (i != 0) // skip first line read, done already when we looked for CN
nextLine = readLine();
if (nextLine.startsWith("SN")) {
nextLine = readLine();
}
if (!parseErrorPoint(nextLine, points, i, lambdaScale))
return null;
}
Shape s = new PolyBase(points);
drc.addShape(s);
}
return drc;
}
else {
System.out.println("Error, expected Edge or Poly definition on line number "+lineno+": "+nextLine);
}
return null;
}
// parse a line specifying an edge, and add it to a list of shapes
// lambdaScale: divide microns by this number to get lambda
private Shape parseErrorEdge(String line, double lambdaScale) {
String [] vals = line.trim().split(spaces);
if (vals.length != 4) {
System.out.println("Error, bad format for edge on line number "+lineno+": "+line);
return null;
}
try {
double x1 = (double)Integer.parseInt(vals[0])/(double)scale/lambdaScale;
double y1 = (double)Integer.parseInt(vals[1])/(double)scale/lambdaScale;
double x2 = (double)Integer.parseInt(vals[2])/(double)scale/lambdaScale;
double y2 = (double)Integer.parseInt(vals[3])/(double)scale/lambdaScale;
Shape line2d = new Line2D.Double(x1, y1, x2, y2);
return line2d;
} catch (NumberFormatException e) {
System.out.println("Error, bad format for edge on line number "+lineno+": "+line);
return null;
}
}
// parse a line specifying a polygon vertex, and add it to a list of points
private boolean parseErrorPoint(String line, Point2D [] points, int point, double lambdaScale) {
String [] vals = line.trim().split(spaces);
if (vals.length != 2) {
System.out.println("Error, bad format for poly vertex on line number "+lineno+": "+line);
return false;
}
try {
double x1 = (double)Integer.parseInt(vals[0])/(double)scale/lambdaScale;
double y1 = (double)Integer.parseInt(vals[1])/(double)scale/lambdaScale;
Point2D p = new Point2D.Double(x1, y1);
points[point] = p;
return true;
} catch (NumberFormatException e) {
System.out.println("Error, bad format for poly vertex on line number "+lineno+": "+line);
return false;
}
}
/**
* Method to create Logger
* @return number of errors if found
*/
private int done() {
try {
in.close();
} catch (IOException e) {}
// populate error logger
logger = ErrorLogger.newInstance("Calibre "+type+" Errors");
int sortKey = 0;
int count = 0;
for (Iterator<DrcRuleViolation> it = ruleViolations.iterator(); it.hasNext(); ) {
DrcRuleViolation v = it.next();
String ruleDesc = v.header.comment.toString().replaceAll("\\n", ";");
if (!ruleDesc.contains(v.ruleNumber)) ruleDesc = v.ruleNumber + ": " + ruleDesc;
int y = 1;
for (Iterator<DrcError> it2 = v.errors.iterator(); it2.hasNext(); ) {
DrcError drcError = it2.next();
Cell cell = drcError.cell;
// List<Object> l = new ArrayList<Object>();
List<EPoint> lineList = new ArrayList<EPoint>();
List<PolyBase> polyList = new ArrayList<PolyBase>();
for (Iterator<Shape> it3 = drcError.shapes.iterator(); it3.hasNext(); ) {
Shape shape = it3.next();
if (shape instanceof Line2D) {
Line2D line = (Line2D)shape;
lineList.add(new EPoint(line.getX1(), line.getY1()));
lineList.add(new EPoint(line.getX2(), line.getY2()));
} else if (shape instanceof PolyBase) {
PolyBase poly = (PolyBase)shape;
polyList.add(poly);
} else {
System.out.println("Unsupported drc error shape "+shape);
}
}
logger.logMessageWithLines(y+". "+cell.getName()+": "+ruleDesc, polyList,
lineList, cell, sortKey, true);
y++;
count++;
}
logger.setGroupName(sortKey, "(" + (y-1) + ") " + ruleDesc);
sortKey++;
}
System.out.println(type+" Imported "+count+" errors from "+filename);
if (count == 0 && !noPopupMessages) {
Job.getUserInterface().showInformationMessage(type+" Imported Zero Errors", type+" Import Complete");
}
logger.termLogging(!noPopupMessages);
return logger.getNumErrors();
}
// -----------------------------------------------------------------------------
private static class DrcRuleViolation {
private final String ruleNumber;
private final Header header;
private final List<DrcError> errors; // list of DrcErrors
private DrcRuleViolation(String ruleNumber, Header header) {
this.ruleNumber = ruleNumber;
this.header = header;
this.errors = new ArrayList<DrcError>();
}
private void addError(DrcError error) {
errors.add(error);
}
}
private static class DrcError {
private final Cell cell;
private final List<Shape> shapes; // list of shapes
private DrcError(Cell cell) {
this.cell = cell;
this.shapes = new ArrayList<Shape>();
}
private void addShape(Shape shape) {
shapes.add(shape);
}
}
private static class Header {
private final int currentDrcResultsCount;
// private final int originalDrcResultsCount;
private final int headerLength; // does not include headerStart line
// private String ruleFilePath;
// private String ruleFileTitle;
private StringBuffer comment;
private Header(int currentDrcResultsCount, int originalDrcResultsCount, int headerLength) {
this.currentDrcResultsCount = currentDrcResultsCount;
// this.originalDrcResultsCount = originalDrcResultsCount;
this.headerLength = headerLength;
comment = new StringBuffer();
}
public void addHeaderLine(String line) {
if (line.startsWith("Rule File Pathname")) {
// ruleFilePath = line;
} else if (line.startsWith("Rule File Title")) {
// ruleFileTitle = line;
} else {
if (comment.length() != 0) {
// already a line added
comment.append("\n");
}
comment.append(line);
}
}
}
// ---------------------------------------------------------
private String readLine() throws IOException {
return readLine(true);
}
private String readLine(boolean errorOnEOF) throws IOException {
// if in is null we ignore
if (in == null) return null;
String line = in.readLine();
if (line == null && errorOnEOF) {
System.out.println("Unexpected End of File!");
in = null; // ignore rest of readLine requests
return null;
}
lineno++;
return line;
}
public static Cell getCell(String cellName, Map<Cell,String> mangledNames) {
List<Cell> matchedNames = new ArrayList<Cell>();
for (Map.Entry<Cell,String> entry : mangledNames.entrySet()) {
String name = entry.getValue();
if (name.equals(cellName))
matchedNames.add(entry.getKey());
}
if (matchedNames.size() == 0) return null;
if (matchedNames.size() == 1) return matchedNames.get(0);
// more than one match, ask user to choose, or just return the first one in non-interactive mode
UserInterface ui = Job.getUserInterface();
String choices[] = new String[matchedNames.size()];
for (int i=0; i<choices.length; i++) {
choices[i] = matchedNames.get(i).describe(false);
}
int c = ui.askForChoice("Multiple cells matches, please choose one for \""+cellName+"\":",
"Ambiguity Found", choices, choices[0]);
return matchedNames.get(c);
}
/*
public static Cell getCell(String cellName) {
// try blind search
for (Iterator<Library> it = Library.getLibraries(); it.hasNext(); ) {
Library lib = (Library)it.next();
Cell c = lib.findNodeProto(cellName+"{lay}");
if (c != null && (c instanceof Cell))
return c;
}
// assume libname.cellname format
if (cellName.indexOf(GDS.concatStr) != -1) {
String libname = cellName.substring(0, cellName.indexOf(GDS.concatStr));
String name = cellName.substring(cellName.indexOf(GDS.concatStr)+1, cellName.length());
Library lib = Library.findLibrary(libname);
if (lib == null) {
// lib name may have been truncated
for (Iterator<Library> it = Library.getLibraries(); it.hasNext(); ) {
Library l = (Library)it.next();
if (l.getName().startsWith(libname)) {
lib = l;
break;
}
}
}
if (lib != null) {
Cell c = lib.findNodeProto(name+"{lay}");
if (c != null && (c instanceof Cell))
return c;
// try taking off any ending _###
if (name.matches("(.+?)_\\d+")) {
int underscore = name.lastIndexOf('_');
c = lib.findNodeProto(name.substring(0, underscore)+"{lay}");
if (c != null && (c instanceof Cell))
return c;
}
}
}
return null;
}
*/
// ======================================================================================
// DRC Density results
public static void readDensityErrors(Cell cell, File drcDirectory) {
if (drcDirectory == null || !drcDirectory.exists() || !drcDirectory.isDirectory()) {
System.out.println("DRC density errors: no such directory: "+drcDirectory.getAbsolutePath());
return;
}
ErrorLogger logger = ErrorLogger.newInstance("Calibre DRC Density Values");
int sortKey = 0;
double scale = cell.getTechnology().getScale();
for (File file : drcDirectory.listFiles()) {
if (file.getName().endsWith(".density")) {
try {
FileReader reader = new FileReader(file);
BufferedReader in = new BufferedReader(reader);
String line = null;
int lineno = 0;
boolean first = true;
while ( (line = in.readLine()) != null) {
lineno++;
if (line.equals("")) continue;
if (first) {
logger.setGroupName(sortKey, file.getName());
first = false;
}
String [] parts = line.split("[ ]+");
if (parts.length != 5) {
System.out.println("Ignoring line "+file.getName()+"."+lineno+": "+line);
continue;
}
double x1, y1, x2, y2;
x1 = Double.valueOf(parts[0]) / scale * 1000;
y1 = Double.valueOf(parts[1]) / scale * 1000;
x2 = Double.valueOf(parts[2]) / scale * 1000;
y2 = Double.valueOf(parts[3]) / scale * 1000;
Point2D [] points = new Point2D[4];
points[0] = new Point2D.Double(x1, y1);
points[1] = new Point2D.Double(x1, y2);
points[2] = new Point2D.Double(x2, y2);
points[3] = new Point2D.Double(x2, y1);
PolyBase poly = new PolyBase(points);
logger.logError(file.getName()+": "+parts[4], poly, cell, sortKey);
}
if (!first) sortKey++;
} catch (IOException e) {
System.out.println("Error read file "+file.getName()+": "+e.getMessage());
}
}
}
logger.termLogging(false);
}
}