/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: NccCellAnnotations.java * * Copyright (c) 2003 Sun Microsystems and 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.ncc.basic; import com.sun.electric.database.hierarchy.Cell; import com.sun.electric.database.hierarchy.Library; import com.sun.electric.database.network.NetworkTool; import com.sun.electric.database.variable.Variable; import com.sun.electric.tool.Job; import com.sun.electric.tool.user.CircuitChangeJobs; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.StringTokenizer; /** Representation of the NCC annotations that a user may place on a Cell */ public class NccCellAnnotations { /** key of Variable holding NCC Cell annotations. */ public static final Variable.Key NCC_ANNOTATION_KEY = Variable.newKey("ATTR_NCC"); public static class NamePattern { private final boolean isRegExp; private final String pattern; NamePattern(boolean isRegEx, String pat) { isRegExp=isRegEx; pattern=pat; } /** String match or regular expression match */ public boolean matches(String name) { return isRegExp ? name.matches(pattern) : name.equals(pattern); } public boolean stringEquals(String name) {return name.equals(pattern);} /** @return If NamePattern is a name then return the name. If * NamePattern is a regular expression then return null. */ public String getName() {return isRegExp ? null : pattern;} } /** I need a special Lexer for name patterns because spaces are * significant inside regular expressions. For example the * pattern: "/foo bar/" contains an embedded space. */ private class NamePatternLexer { private final String s; private int pos=0; private int findWhite() { while (true) { if (pos>=s.length()) return -1; if (Character.isWhitespace(s.charAt(pos))) return pos; pos++; } } /** @return index of first white char or -1 if no white chars * between here and end of line */ private int findNonWhite() { while (true) { if (pos>=s.length()) return -1; if (!Character.isWhitespace(s.charAt(pos))) return pos; pos++; } } private int findSlash() { while (true) { if (pos>=s.length()) return -1; if (s.charAt(pos)=='/') return pos; pos++; } } public NamePatternLexer(String annot) {s = annot;} /** @return null when no more patterns left */ public NamePattern nextPattern() { int startTok = findNonWhite(); if (startTok==-1) return null; int endTok; if (s.charAt(startTok)=='/') { pos++; // skip the leading '/' startTok = pos; endTok = findSlash(); if (endTok==-1) { prErr("Regular Expression has no trailing '/': "+ s.substring(startTok-1)+"."); endTok = s.length(); } else { pos++; // skip the trailing '/' } String pat = s.substring(startTok, endTok); return new NamePattern(true, pat); } else { endTok = findWhite(); if (endTok==-1) endTok = s.length(); return new NamePattern(false, s.substring(startTok, endTok)); } } /** @return everything not parsed by nextPattern() */ public String restOfLine() {return s.substring(pos);} } /** used for error messages */ private String cellThatOwnsMe; /** unprocessed annotation text */ private List<String> annotText = new ArrayList<String>(); /** NamePatterns matching Exports connected by parent cell */ private List<List<NamePattern>> exportsConnByParent = new ArrayList<List<NamePattern>>(); /** NamePatterns matching Exports to ignore */ private List<List<NamePattern>> exportsToIgnore = new ArrayList<List<NamePattern>>(); /** reason given by user for skipping NCC of this Cell */ private String skipReason; /** reason given by user for treating this Cell as a subcircuit * during hierarchical NCC*/ private String notSubcircuitReason; /** the CellGroup that this cell should join */ private Cell.CellGroup groupToJoin; /** NamePatterns matching instance names to flatten */ private List<NamePattern> flattenInstances = new ArrayList<NamePattern>(); /** NamePatterns matching Export names that need renaming */ private List<NamePattern> exportsToRename = new ArrayList<NamePattern>(); /** Reason why we should treat this Cell as a black box */ private String blackBoxReason; /** Cell annotation describing type of contained MOS */ private String transistorType; /** Cell annotation describing type of contained resistor */ private String resistorType; /** List of names of Wires to force matches between schematic and layout*/ private List<String> forceWireMatches = new ArrayList<String>(); /** List of names of Wires to force matches between schematic and layout*/ private List<String> forcePartMatches = new ArrayList<String>(); private void processExportsConnAnnot(NamePatternLexer lex) { List<NamePattern> connected = new ArrayList<NamePattern>(); for (NamePattern np=lex.nextPattern(); np!=null; np=lex.nextPattern()) { connected.add(np); } if (connected.size()>0) exportsConnByParent.add(connected); } private void processExportsToIgnoreAnnot(NamePatternLexer lex) { List<NamePattern> ignore = new ArrayList<NamePattern>(); for (NamePattern np=lex.nextPattern(); np!=null; np=lex.nextPattern()) { ignore.add(np); } if (ignore.size()>0) exportsToIgnore.add(ignore); } private void processSkipAnnotation(NamePatternLexer lex) { skipReason = lex.restOfLine(); } private void processNotSubcircuitAnnotation(NamePatternLexer lex) { notSubcircuitReason = lex.restOfLine(); } private void prErr(String s) { String currAnnot = annotText.get(annotText.size()-1); System.out.println(" "+s+" cell= "+cellThatOwnsMe+" annotation= "+currAnnot); } private void processJoinGroupAnnotation(String note) { StringTokenizer lex = new StringTokenizer(note); lex.nextToken(); // skip keyword if (!lex.hasMoreTokens()) { prErr("joinGroup lacks Library:Cell argument."); return; } String libCell = lex.nextToken(); int colon = libCell.indexOf(':'); if (colon==-1) { prErr("Group specification must be of form Library:Cell{view}."); return; } String libName = libCell.substring(0, colon); String cellName = libCell.substring(colon+1); Library lib = Library.findLibrary(libName); if (lib==null) { prErr("Can't find library: "+libName+"."); return; } Cell cell = lib.findNodeProto(cellName); if (cell==null) { prErr("Can't find Cell "+cellName+"."); return; } groupToJoin = cell.getCellGroup(); Job.error(groupToJoin==null, "null cell group?"); } private void processFlattenInstancesAnnotation(NamePatternLexer lex) { for (NamePattern np=lex.nextPattern(); np!=null; np=lex.nextPattern()) { flattenInstances.add(np); } } private void processExportsToRenameAnnotation(NamePatternLexer lex) { for (NamePattern np=lex.nextPattern(); np!=null; np=lex.nextPattern()) { exportsToRename.add(np); } } private void processBlackBox(NamePatternLexer lex) { blackBoxReason = lex.restOfLine(); } private void processTransistorType(NamePatternLexer lex) { NamePattern type = lex.nextPattern(); if (type==null) { prErr("Bad transistorType annotation: missing type"); return; } NamePattern type2 = lex.nextPattern(); if (type2!=null) { prErr("Bad transistorType annotation: only one type allowed"); return; } if (transistorType!=null) { prErr("only one transistorType annotation allowed per Cell"); return; } transistorType = type.getName(); if (transistorType==null) { prErr("Transistor type may not be a regular expression"); return; } } private void processResistorType(NamePatternLexer lex) { NamePattern type = lex.nextPattern(); if (type==null) { prErr("Bad resistorType annotation: missing type"); return; } NamePattern type2 = lex.nextPattern(); if (type2!=null) { prErr("Bad resistorType annotation: only one type allowed"); return; } if (resistorType!=null) { prErr("only one resistorType annotation allowed per Cell"); return; } resistorType = type.getName(); if (resistorType==null) { prErr("resistor type may not be a regular expression"); return; } } private void processForceWireMatch(NamePatternLexer lex) { NamePattern wireNamePat = lex.nextPattern(); if (wireNamePat==null) { prErr("Bad forceWireMatch annotation: missing Wire name"); return; } String wireName = wireNamePat.getName(); if (wireName==null) { prErr("Bad forceWireMatch annotation: Wire name may not be a regular expression"); return; } forceWireMatches.add(wireName); NamePattern namePat2 = lex.nextPattern(); if (namePat2!=null) { prErr("Bad forceWireMatch annotation: only one Wire name allowed"); } } private void processForcePartMatch(NamePatternLexer lex) { NamePattern partNamePat = lex.nextPattern(); if (partNamePat==null) { prErr("Bad forcePartMatch annotation: missing Part name"); return; } String partName = partNamePat.getName(); if (partName==null) { prErr("Bad forcePartMatch annotation: Part name may not be a regular expression"); return; } forcePartMatches.add(partName); NamePattern namePat2 = lex.nextPattern(); if (namePat2!=null) { prErr("Bad forcePartMatch annotation: only one Part name allowed"); } } private void doAnnotation(String note) { annotText.add(note); // for prErr() NamePatternLexer lex = new NamePatternLexer(note); NamePattern key = lex.nextPattern(); if (key==null) { // skip blank lines } else if (key.stringEquals("exportsConnectedByParent")) { processExportsConnAnnot(lex); } else if (key.stringEquals("exportsToIgnore")) { processExportsToIgnoreAnnot(lex); } else if (key.stringEquals("skipNCC")) { processSkipAnnotation(lex); } else if (key.stringEquals("notSubcircuit")) { processNotSubcircuitAnnotation(lex); } else if (key.stringEquals("joinGroup")) { processJoinGroupAnnotation(note); } else if (key.stringEquals("flattenInstances")) { processFlattenInstancesAnnotation(lex); } else if (key.stringEquals("exportsToRename")) { processExportsToRenameAnnotation(lex); } else if (key.stringEquals("blackBox")) { processBlackBox(lex); } else if (key.stringEquals("transistorType")) { processTransistorType(lex); } else if (key.stringEquals("resistorType")) { processResistorType(lex); } else if (key.stringEquals("forceWireMatch")) { processForceWireMatch(lex); } else if (key.stringEquals("forcePartMatch")) { processForcePartMatch(lex); } else { prErr("Unrecognized NCC annotation."); } } private NccCellAnnotations(Cell cell, Object annotation) { cellThatOwnsMe = NccUtils.fullName(cell); // for prErr() if (annotation instanceof String) { doAnnotation((String) annotation); } else if (annotation instanceof String[]) { String[] ss = (String[]) annotation; for (int i=0; i<ss.length; i++) doAnnotation(ss[i]); } else { prErr(" ignoring bad NCC annotation: "); } } // ---------------------- public methods ----------------------------- /** * Method to create NCC annotations in the current Cell. * Called from the menu commands. */ public static void makeNCCAnnotationMenuCommand(String newAnnotation) { CircuitChangeJobs.MakeCellAnnotationJob.makeAnnotationMenuCommand(NetworkTool.getNetworkTool(), NCC_ANNOTATION_KEY, newAnnotation); } /** Add an NCC annotation to a Cell. */ public static void addNccAnnotation(Cell c, String newAnnotation) { CircuitChangeJobs.MakeCellAnnotationJob.addAnnotation(c, NCC_ANNOTATION_KEY, newAnnotation); } /** * Method to get the NCC annotations on a Cell. * @param cell the Cell to query. * @return the NccCellAnnotations for the Cell. * Returns null if the Cell has no NCC annotations */ public static NccCellAnnotations getAnnotations(Cell cell) { Variable nccVar = cell.getVar(NCC_ANNOTATION_KEY); if (nccVar==null) return null; Object annotation = nccVar.getObject(); return new NccCellAnnotations(cell, annotation); } /** @return a String which is the reason given by the user for not * NCCing the cell or null if there is no skipNCC annotation on * the cell. */ public String getSkipReason() {return skipReason;} /** @return the reason given by the user for not treating this Cell * as a subcircuit during hierarchical NCC. Return null if there is * no notSubcircuitReason annotation on the Cell */ public String getNotSubcircuitReason() {return notSubcircuitReason;} /** @return an Iterator over Lists of NamePatterns. Each List specifies * the names (or regular expressions that match the names) of Exports * that the user expects to be connected by the Cell's parent. */ public Iterator<List<NamePattern>> getExportsConnected() {return exportsConnByParent.iterator();} /** @return an Iterator over Lists of NamePatterns. Each List specifies * the names (or regular expressions that match the names) of Exports * that the user wants to be ignored. */ public Iterator<List<NamePattern>> getExportsToIgnore() { return exportsToIgnore.iterator(); } public Iterator<String> getAnnotationText() {return annotText.iterator();} /** @return the CellGroup specified by a joinGroup annotation */ public Cell.CellGroup getGroupToJoin() {return groupToJoin;} /** @return true if a flattenInstance annotation says to flatten instName */ public boolean flattenInstance(String instName) { for (NamePattern pattern : flattenInstances) { if (pattern.matches(instName)) return true; } return false; } public boolean renameExport(String exportName) { for (NamePattern pattern : exportsToRename) { if (pattern.matches(exportName)) return true; } return false; } /** @return the reason given by the user for block boxing this Cell. * return null if there is no blackBox annotation. */ public String getBlackBoxReason() {return blackBoxReason;} /** @return the transistor type if Cell has a transitorType annotation. * Otherwise return null. */ public String getTransistorType() {return transistorType;} /** @return the resistor type if Cell has a resistorType annotation. * Otherwise return null. */ public String getResistorType() {return resistorType;} /** @return the names of Wires for which we should force matches */ public List<String> getForceWireMatches() {return forceWireMatches;} /** @return the names of Wires for which we should force matches */ public List<String> getForcePartMatches() {return forcePartMatches;} }