/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: CompileVerilogStruct.java * Compile Structural Verilog to a netlist * * Copyright (c) 2010, 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.user; import com.sun.electric.database.EditingPreferences; import com.sun.electric.database.ImmutableArcInst; 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.Library; 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.prototype.NodeProto; import com.sun.electric.database.prototype.PortProto; 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.technology.ArcProto; import com.sun.electric.technology.technologies.Generic; import com.sun.electric.tool.Job; import com.sun.electric.util.TextUtils; import com.sun.electric.util.math.DBMath; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Date; 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 structural Verilog compiler. */ public class CompileVerilogStruct { private List<VModule> allModules; private int errorCount; private boolean hasErrors; private VModule curModule; private List<TokenList> tList; private int tokenIndex; /********** Scanner Token Types ******************************************/ public static class TokenType { private String name, str; private TokenType(String name, String str) { this.name = name; this.str = str; } public String getName() { return name; } public String getChar() { return str; } public static final TokenType LEFTPAREN = new TokenType("Left Parenthesis", "("); public static final TokenType RIGHTPAREN = new TokenType("Right Parenthesis", ")"); public static final TokenType LEFTBRACKET = new TokenType("Left Bracket", "["); public static final TokenType RIGHTBRACKET = new TokenType("Right Bracket", "]"); public static final TokenType LEFTBRACE = new TokenType("Left Brace", "{"); public static final TokenType RIGHTBRACE = new TokenType("Right Brace", "}"); public static final TokenType SLASH = new TokenType("Forward Slash", "/"); public static final TokenType COMMA = new TokenType("Comma", ","); public static final TokenType MINUS = new TokenType("Minus", "-"); public static final TokenType PERIOD = new TokenType("Period", "."); public static final TokenType COLON = new TokenType("Colon", ":"); public static final TokenType SEMICOLON = new TokenType("Semicolon", ";"); public static final TokenType DOUBLEDOT = new TokenType("DotDot", ".."); public static final TokenType VARASSIGN = new TokenType("Assign", "=>"); public static final TokenType UNKNOWN = new TokenType("Unknown", ""); public static final TokenType IDENTIFIER = new TokenType("Identifier", ""); public static final TokenType KEYWORD = new TokenType("Keyword", ""); public static final TokenType DECIMAL = new TokenType("Decimal Number", ""); public static final TokenType CHAR = new TokenType("Character", ""); public static final TokenType STRING = new TokenType("String", ""); } /********** Scanner Tokens *****************************************/ public class TokenList { /** token number */ TokenType type; /** NULL if delimiter, * pointer to global name space if identifier, * pointer to keyword table if keyword, * pointer to string if decimal literal, * pointer to string if based literal, * value of character if character literal, * pointer to string if string literal, * pointer to string if bit string literal */ Object pointer; /** TRUE if space before next token */ boolean space; /** line number token occurred */ int lineNum; private TokenList(TokenType type, Object pointer, int lineNum, boolean space) { this.type = type; this.pointer = pointer; this.lineNum = lineNum; this.space = true; tList.add(this); } public int makeErrorLine(StringBuffer buffer) { int index = tList.indexOf(this); int lineNumber = this.lineNum; // back up to start of line while (index > 0 && tList.get(index-1).lineNum == lineNumber) index--; // form line in buffer int pointer = 0; for(int i=index; i<tList.size(); i++) { TokenList tok = tList.get(i); if (tok.lineNum != lineNumber) break; if (tok == this) pointer = buffer.length(); buffer.append(tok.toString()); if (tok.space) buffer.append(" "); } return pointer; } public String toString() { if (type == TokenType.STRING) return "\"" + pointer + "\" "; if (type == TokenType.KEYWORD) return ((VKeyword)pointer).name; if (type == TokenType.DECIMAL) return (String)pointer; if (type == TokenType.CHAR) return ((Character)pointer).charValue() + ""; if (type == TokenType.IDENTIFIER) { if (pointer == null) return "NULL"; return pointer.toString(); } return type.getChar(); } } private void resetTokenListPointer() { tokenIndex = 0; } private TokenList getNextToken() { if (tokenIndex >= tList.size()) return null; TokenList token = tList.get(tokenIndex++); return token; } private TokenList peekNextToken() { if (tokenIndex >= tList.size()) return null; return tList.get(tokenIndex); } private TokenType getTokenType(TokenList token) { if (token == null) return TokenType.UNKNOWN; return token.type; } private TokenList needNextToken(TokenType type) { TokenList token = getNextToken(); if (token == null) { reportErrorMsg(null, "End of file encountered"); return null; } if (token.type != type) { reportErrorMsg(token, "Expecting a " + type.getName()); parseToSemicolon(); return null; } return token; } /********** Keywords ******************************************/ public static class VKeyword { /** string defining keyword */ String name; private static List<VKeyword> theKeywords = new ArrayList<VKeyword>(); VKeyword(String name) { this.name = name; theKeywords.add(this); } public static VKeyword findKeyword(String tString) { for(VKeyword vk : theKeywords) { if (vk.name.equals(tString)) return vk; } return null; } public static final VKeyword ASSIGN = new VKeyword("assign"); public static final VKeyword ENDMODULE = new VKeyword("endmodule"); public static final VKeyword INOUT = new VKeyword("inout"); public static final VKeyword INPUT = new VKeyword("input"); public static final VKeyword MODULE = new VKeyword("module"); public static final VKeyword OUTPUT = new VKeyword("output"); public static final VKeyword SUPPLY = new VKeyword("supply"); public static final VKeyword SUPPLY0 = new VKeyword("supply0"); public static final VKeyword WIRE = new VKeyword("wire"); }; /********** Modules *************************************************/ public class VModule { /** name of entity */ String name; /** true if cell in Verilog */ boolean defined; /** cell of this module */ Cell cell; /** list of ports */ List<FormalPort> ports; /** list of internal wires */ List<String> wires; /** list of instances */ List<Instance> instances; /** networks in the module */ Map<String,List<LocalPort>> allNetworks; VModule(String name, boolean defined) { this.name = name; this.defined = defined; cell = null; if (!defined) { // find the cell for(Library lib : Library.getVisibleLibraries()) { for(Iterator<Cell> it = lib.getCells(); it.hasNext(); ) { Cell libCell = it.next(); if (libCell.getName().equals(name)) { for(Iterator<Cell> gIt = libCell.getCellGroup().getCells(); gIt.hasNext(); ) { Cell rightOne = gIt.next(); if (rightOne.getView() == View.LAYOUT) { cell = rightOne; break; } } if (cell == null) { cell = libCell; break; } } } if (cell != null) break; } } ports = new ArrayList<FormalPort>(); wires = new ArrayList<String>(); instances = new ArrayList<Instance>(); allNetworks = new HashMap<String,List<LocalPort>>(); allModules.add(this); } }; private VModule findModule(String name) { for(VModule mod : allModules) if (mod.name.equals(name)) return mod; return null; } /********** Ports on Modules **********************************/ private static final int MODE_UNKNOWN = 0; private static final int MODE_IN = 1; private static final int MODE_OUT = 2; private static final int MODE_INOUT = 3; public static class FormalPort { /** name of port */ String name; /** mode of port */ int mode; /** range of port */ int firstIndex, secondIndex; public FormalPort(String name) { this.name = name; mode = MODE_UNKNOWN; firstIndex = secondIndex = -1; } }; /********** Instances **********************************************/ public static class Instance { VModule module; String instanceName; private Map<LocalPort,String> ports; public Instance(VModule module, String instanceName) { this.module = module; this.instanceName = instanceName; ports = new HashMap<LocalPort,String>(); } public void addPort(LocalPort lp, String signalName) { ports.put(lp, signalName); } } /********** Ports on Instances **********************************/ public static class LocalPort { /** Instance */ Instance in; /** name of port */ String portName; public LocalPort(Instance in, String portName) { this.in = in; this.portName = portName; } }; /** * The constructor compiles the Verilog and produces a netlist. */ public CompileVerilogStruct(Cell verilogCell) { String [] strings = verilogCell.getTextViewContents(); if (strings == null) { System.out.println("Cell " + verilogCell.describe(true) + " has no text in it"); return; } Job.getUserInterface().startProgressDialog("Compiling Verilog", null); Job.getUserInterface().setProgressNote("Scanning..."); allModules = new ArrayList<VModule>(); tList = new ArrayList<TokenList>(); errorCount = 0; hasErrors = false; doScanner(strings); Job.getUserInterface().setProgressNote("Parsing..."); Job.getUserInterface().setProgressValue(0); doParser(); Job.getUserInterface().stopProgressDialog(); // make network list for(VModule module : allModules) { for(Instance in : module.instances) { for(LocalPort lp : in.ports.keySet()) { String signalName = in.ports.get(lp); List<LocalPort> portsOnNet = module.allNetworks.get(signalName); if (portsOnNet == null) module.allNetworks.put(signalName, portsOnNet = new ArrayList<LocalPort>()); portsOnNet.add(lp); } } } // dumpData(); } // private void dumpData() // { // // write what was found // for(VModule vm : allModules) // { // System.out.println(); // System.out.print("++++ MODULE "+vm.name+"("); // boolean first = true; // for(FormalPort fp : vm.ports) // { // if (!first) System.out.print(", "); // first = false; // System.out.print(fp.name); // if (fp.firstIndex != -1 && fp.secondIndex != -1) // System.out.print("[" + fp.firstIndex + ":" + fp.secondIndex + "]"); // switch (fp.mode) // { // case MODE_IN: System.out.print(" input"); break; // case MODE_OUT: System.out.print(" output"); break; // case MODE_INOUT: System.out.print(" inout"); break; // } // } // System.out.println(")"); // if (!vm.defined) // { // if (vm.cell == null) System.out.println(" CELL NOT FOUND"); else // System.out.println(" CELL IS "+vm.cell.describe(false)); // } // for(Instance in : vm.instances) // { // System.out.print(" INSTANCE "+in.instanceName+" OF CELL "+in.module.name+"("); // first = true; // for(LocalPort lp : in.ports.keySet()) // { // if (!first) System.out.print(", "); // first = false; // String netName = in.ports.get(lp); // System.out.print(lp.portName+"="+netName); // } // System.out.println(")"); // } // for(String netName : vm.allNetworks.keySet()) // { // System.out.print(" NETWORK " + netName + " ON"); // List<LocalPort> ports = vm.allNetworks.get(netName); // for(LocalPort lp : ports) // System.out.print(" " + lp.in.instanceName+":"+lp.portName); // System.out.println(); // } // } // } /** * Method to report whether the Verilog compile was successful. * @return true if there were errors. */ public boolean hasErrors() { return hasErrors; }; /******************************** THE VERILOG SCANNER ********************************/ /** * Method to do lexical scanning of input Verilog and create token list. */ private void doScanner(String [] strings) { String buf = ""; int bufPos = 0; int lineNum = 0; boolean space = false; for(;;) { if (bufPos >= buf.length()) { if (lineNum >= strings.length) return; buf = strings[lineNum++]; if ((lineNum%100) == 0) Job.getUserInterface().setProgressValue( lineNum * 100 / strings.length); bufPos = 0; space = true; } else { if (Character.isWhitespace(buf.charAt(bufPos))) space = true; else space = false; } while (bufPos < buf.length() && Character.isWhitespace(buf.charAt(bufPos))) bufPos++; if (bufPos >= buf.length()) continue; char c = buf.charAt(bufPos); if (Character.isLetter(c)) { // could be identifier (keyword) or bit string literal int end = bufPos; for(; end < buf.length(); end++) { char eChar = buf.charAt(end); if (!Character.isLetterOrDigit(eChar) && eChar != '_') break; } // got alphanumeric from c to end - 1 VKeyword key = VKeyword.findKeyword(buf.substring(bufPos, end)); if (key != null) { new TokenList(TokenType.KEYWORD, key, lineNum, space); } else { String ident = buf.substring(bufPos, end); new TokenList(TokenType.IDENTIFIER, ident, lineNum, space); } bufPos = end; } else if (TextUtils.isDigit(c)) { // could be decimal or based literal int end = bufPos+1; for(; end < buf.length(); end++) { char eChar = buf.charAt(end); if (!TextUtils.isDigit(eChar) && eChar != '_') break; } // got numeric from c to end - 1 new TokenList(TokenType.DECIMAL, buf.substring(bufPos, end), lineNum, space); bufPos = end; } else { switch (c) { case '\\': // backslash starts a quoted identifier int end = bufPos + 1; while (end < buf.length() && buf.charAt(end) != '\n') { if (Character.isWhitespace(buf.charAt(end))) break; end++; } // identifier from c + 1 to end - 1 String ident = buf.substring(bufPos + 1, end); new TokenList(TokenType.IDENTIFIER, ident, lineNum, space); bufPos = end; break; case '/': // got a slash...look for "//" comment end = bufPos + 1; if (end >= buf.length() || buf.charAt(end) != '/') { new TokenList(TokenType.SLASH, null, lineNum, space); bufPos++; break; } // comment: skip to end of line while (end < buf.length() && buf.charAt(end) != '\n') end++; if (end < buf.length() && buf.charAt(end) == '\n') end++; bufPos = end; break; case '"': // got a start of a string end = bufPos + 1; while (end < buf.length() && buf.charAt(end) != '\n') { if (buf.charAt(end) == '"') { if (end+1 < buf.length() && buf.charAt(end+1) == '"') end++; else break; } end++; } // string from c + 1 to end - 1 String newString = buf.substring(bufPos + 1, end); newString.replaceAll("\"\"", "\""); new TokenList(TokenType.STRING, newString, lineNum, space); if (buf.charAt(end) == '"') end++; bufPos = end; break; case '\'': // character literal if (bufPos+2 < buf.length() && buf.charAt(bufPos+2) == '\'') { new TokenList(TokenType.CHAR, new Character(buf.charAt(bufPos+1)), lineNum, space); bufPos += 3; } else bufPos++; break; case '(': new TokenList(TokenType.LEFTPAREN, null, lineNum, space); bufPos++; break; case ')': new TokenList(TokenType.RIGHTPAREN, null, lineNum, space); bufPos++; break; case '[': new TokenList(TokenType.LEFTBRACKET, null, lineNum, space); bufPos++; break; case ']': new TokenList(TokenType.RIGHTBRACKET, null, lineNum, space); bufPos++; break; case '{': new TokenList(TokenType.LEFTBRACE, null, lineNum, space); bufPos++; break; case '}': new TokenList(TokenType.RIGHTBRACE, null, lineNum, space); bufPos++; break; case ',': new TokenList(TokenType.COMMA, null, lineNum, space); bufPos++; break; case '-': if (bufPos+1 < buf.length() && buf.charAt(bufPos+1) == '-') { // got a comment, throw away rest of line bufPos = buf.length(); } else { // got a minus sign new TokenList(TokenType.MINUS, null, lineNum, space); bufPos++; } break; case '.': // could be PERIOD or DOUBLEDOT if (bufPos+1 < buf.length() && buf.charAt(bufPos+1) == '.') { new TokenList(TokenType.DOUBLEDOT, null, lineNum, space); bufPos += 2; } else { new TokenList(TokenType.PERIOD, null, lineNum, space); bufPos++; } break; case ':': // could be COLON or VARASSIGN if (bufPos+1 < buf.length() && buf.charAt(bufPos+1) == '=') { new TokenList(TokenType.VARASSIGN, null, lineNum, space); bufPos += 2; } else { new TokenList(TokenType.COLON, null, lineNum, space); bufPos++; } break; case ';': new TokenList(TokenType.SEMICOLON, null, lineNum, space); bufPos++; break; default: new TokenList(TokenType.UNKNOWN, null, lineNum, space); bufPos++; break; } } } } /******************************** THE VERILOG PARSER ********************************/ /** * Method to parse the token list. * Reports on any syntax errors and create the required syntax trees. */ private void doParser() { curModule = null; resetTokenListPointer(); int tokenCount = 0; for(;;) { TokenList token = getNextToken(); if (token == null) break; tokenCount++; if ((tokenCount%100) == 0) Job.getUserInterface().setProgressValue( tokenIndex * 100 / tList.size()); if (token.type == TokenType.KEYWORD) { VKeyword vk = (VKeyword)token.pointer; if (vk == VKeyword.MODULE) { curModule = parseModule(); continue; } if (vk == VKeyword.ENDMODULE) { curModule = null; continue; } if (vk == VKeyword.INPUT || vk == VKeyword.OUTPUT || vk == VKeyword.INOUT || vk == VKeyword.WIRE || vk == VKeyword.SUPPLY || vk == VKeyword.SUPPLY0) { parseDeclare(token); continue; } if (vk == VKeyword.ASSIGN) { parseAssign(token); continue; } reportErrorMsg(token, "Unknown keyword"); } else if (token.type == TokenType.IDENTIFIER) { // identifier: parse as an instance declaration if (curModule == null) { reportErrorMsg(token, "Instance declaration is not inside a Module"); parseToSemicolon(); break; } Instance inst = parseInstance(token); if (inst != null) curModule.instances.add(inst); } else { reportErrorMsg(token, "Expecting an identifier"); parseToSemicolon(); } } // fill in ports for modules that were not generated for(VModule module : allModules) { for(Instance in : module.instances) { for(LocalPort lp : in.ports.keySet()) { boolean found = false; for(FormalPort subPort : in.module.ports) { if (subPort.name.equals(lp.portName)) { found = true; break; } } if (!found) { FormalPort fp = new FormalPort(lp.portName); in.module.ports.add(fp); } } } } } /** * Method to parse a module description of the form: * module IDENTIFIER (FORMAL_PORT_LIST); * FORMAL_PORT_LIST ::= [IDENTIFIER {, IDENTIFIER}] */ private VModule parseModule() { // check for entity IDENTIFIER TokenList token = needNextToken(TokenType.IDENTIFIER); if (token == null) return null; String name = (String)token.pointer; VModule module = findModule(name); if (module != null) { reportErrorMsg(token, "Module already exists"); parseToSemicolon(); return null; } module = new VModule(name, true); // check for opening bracket of FORMAL_PORT_LIST token = needNextToken(TokenType.LEFTPAREN); if (token == null) return null; // gather FORMAL_PORT_LIST for(;;) { token = needNextToken(TokenType.IDENTIFIER); if (token == null) return null; FormalPort port = new FormalPort((String)token.pointer); module.ports.add(port); token = getNextToken(); if (getTokenType(token) != TokenType.COMMA) break; } // check for closing bracket of FORMAL_PORT_LIST if (getTokenType(token) != TokenType.RIGHTPAREN) { reportErrorMsg(token, "Expecting a right parenthesis"); parseToSemicolon(); return null; } // check for SEMICOLON token = needNextToken(TokenType.SEMICOLON); if (token == null) return null; return module; } private void parseAssign(TokenList token) { parseToSemicolon(); return; } private void parseDeclare(TokenList declareToken) { if (curModule == null) { reportErrorMsg(declareToken, "Not in a module"); parseToSemicolon(); return; } int mode = MODE_IN; VKeyword vk = (VKeyword)declareToken.pointer; if (vk == VKeyword.OUTPUT) mode = MODE_OUT; else if (vk == VKeyword.INOUT) mode = MODE_INOUT; TokenList token = getNextToken(); int firstRange = -1, secondRange = -1; if (getTokenType(token) == TokenType.LEFTBRACKET) { // a bus of bits token = getNextToken(); firstRange = TextUtils.atoi((String)token.pointer); token = needNextToken(TokenType.COLON); if (token == null) return; token = getNextToken(); secondRange = TextUtils.atoi((String)token.pointer); token = needNextToken(TokenType.RIGHTBRACKET); if (token == null) return; token = getNextToken(); } // now get the list of identifiers for(;;) { if (getTokenType(token) != TokenType.IDENTIFIER) { reportErrorMsg(token, "Expected identifier"); parseToSemicolon(); return; } String idName = (String)token.pointer; boolean found = false; if (vk == VKeyword.WIRE || vk == VKeyword.SUPPLY || vk == VKeyword.SUPPLY0) { if (firstRange != -1 && secondRange != -1) { if (firstRange > secondRange) { int swap = firstRange; firstRange = secondRange; secondRange = swap; } for(int i=firstRange; i<=secondRange; i++) { String realName = idName + "[" + i + "]"; if (curModule.wires.contains(realName)) { reportErrorMsg(token, "Identifier " + realName + " defined twice"); parseToSemicolon(); return; } curModule.wires.add(realName); } } else { if (curModule.wires.contains(idName)) { reportErrorMsg(token, "Identifier defined twice"); parseToSemicolon(); return; } curModule.wires.add(idName); } } else { for(FormalPort fp : curModule.ports) { if (fp.name.equals(idName)) { fp.mode = mode; fp.firstIndex = firstRange; fp.secondIndex = secondRange; found = true; break; } } if (!found) { reportErrorMsg(token, "Unknown identifier"); parseToSemicolon(); return; } } // look for comma token = getNextToken(); if (getTokenType(token) == TokenType.COMMA) { token = getNextToken(); continue; } if (getTokenType(token) == TokenType.SEMICOLON) break; reportErrorMsg(token, "Unknown separator between identifiers"); parseToSemicolon(); return; } } private Instance parseInstance(TokenList token) { // get the cell name from the first token String cellName = (String)token.pointer; // get the instance name from the second token token = needNextToken(TokenType.IDENTIFIER); if (token == null) return null; String instanceName = (String)token.pointer; // must then be followed by an open parenthesis to start the argument list token = needNextToken(TokenType.LEFTPAREN); if (token == null) return null; VModule module = findModule(cellName); if (module == null) module = new VModule(cellName, false); Instance inst = new Instance(module, instanceName); // get the arguments int argNum = 1; for(;;) { token = getNextToken(); if (getTokenType(token) == TokenType.RIGHTPAREN) break; // guess at the name of the next port String portName = "ARG" + argNum; argNum++; String signalName = null; if (getTokenType(token) == TokenType.PERIOD) { token = needNextToken(TokenType.IDENTIFIER); if (token == null) return null; portName = (String)token.pointer; token = needNextToken(TokenType.LEFTPAREN); if (token == null) return null; // either an identifier or a group of identifiers enclosed in braces token = getNextToken(); if (getTokenType(token) == TokenType.LEFTBRACE) { int index = 0; if (inst.module.cell != null) { int lowestIndex = Integer.MAX_VALUE; int portNameLen = portName.length(); for(Iterator<Export> it = inst.module.cell.getExports(); it.hasNext(); ) { Export e = it.next(); if (e.getName().startsWith(portName) && e.getName().length() > portNameLen) { if (e.getName().charAt(portNameLen) == '[') { int thisIndex = TextUtils.atoi(e.getName().substring(portNameLen+1)); if (thisIndex < lowestIndex) lowestIndex = thisIndex; } } } index = lowestIndex; } for(;;) { token = getNextToken(); if (getTokenType(token) != TokenType.IDENTIFIER) { reportErrorMsg(token, "Expecting an identifier"); parseToSemicolon(); return null; } signalName = getSignalName(token); inst.addPort(new LocalPort(inst, portName + "[" + index + "]"), signalName); index++; token = getNextToken(); if (getTokenType(token) == TokenType.RIGHTBRACE) break; if (getTokenType(token) != TokenType.COMMA) { reportErrorMsg(token, "Expecting a comma"); parseToSemicolon(); return null; } } } else { // single port if (getTokenType(token) != TokenType.IDENTIFIER) { reportErrorMsg(token, "Expecting an identifier"); parseToSemicolon(); return null; } signalName = getSignalName(token); inst.addPort(new LocalPort(inst, portName), signalName); } token = needNextToken(TokenType.RIGHTPAREN); if (token == null) return null; } else if (getTokenType(token) == TokenType.IDENTIFIER) { signalName = getSignalName(token); inst.addPort(new LocalPort(inst, portName), signalName); } else { reportErrorMsg(token, "Unknown separator between identifiers"); parseToSemicolon(); return null; } token = getNextToken(); if (getTokenType(token) == TokenType.RIGHTPAREN) break; if (getTokenType(token) != TokenType.COMMA) { reportErrorMsg(token, "Expecting a comma"); parseToSemicolon(); return null; } } // must end with a semicolon token = needNextToken(TokenType.SEMICOLON); if (token == null) return null; return inst; } private String getSignalName(TokenList token) { if (getTokenType(token) != TokenType.IDENTIFIER) return null; String signalName = (String)token.pointer; // see if it is indexed TokenList next = peekNextToken(); if (getTokenType(next) == TokenType.LEFTBRACKET) { // indexed signal getNextToken(); TokenList index = needNextToken(TokenType.DECIMAL); if (index == null) return null; TokenList close = needNextToken(TokenType.RIGHTBRACKET); if (close == null) return null; signalName += "[" + (String)index.pointer + "]"; } return signalName; } /** * Method to ignore up to the next semicolon. */ private void parseToSemicolon() { for(;;) { TokenList token = getNextToken(); if (token.type == TokenType.SEMICOLON) break; } } private void reportErrorMsg(TokenList tList, String errMsg) { hasErrors = true; errorCount++; if (errorCount == 30) System.out.println("TOO MANY ERRORS...PRINTING NO MORE"); if (errorCount >= 30) return; if (tList == null) { System.out.println("ERROR " + errMsg); return; } System.out.println("ERROR on line " + tList.lineNum + ", " + errMsg + ":"); // back up to start of line StringBuffer buffer = new StringBuffer(); int pointer = tList.makeErrorLine(buffer); // print out line System.out.println(buffer.toString()); // print out pointer buffer = new StringBuffer(); for (int i = 0; i < pointer; i++) buffer.append(" "); System.out.println(buffer.toString() + "^"); } /******************************** THE RATS-NEST CELL GENERATOR ********************************/ /** * Method to generate a cell that represents this netlist. * @param destLib destination library. */ public Cell genCell(Library destLib) { if (hasErrors) return null; Map<NodeProto,Map<PortProto,Point2D>> portLocMap = new HashMap<NodeProto,Map<PortProto,Point2D>>(); Job.getUserInterface().startProgressDialog("Building Rats-Nest Cells", null); for(VModule mod : allModules) { if (!mod.defined) continue; String cellName = mod.name + "{lay}"; System.out.println("Creating cell " + cellName); Job.getUserInterface().setProgressNote("Creating Nodes in Cell " + cellName); Job.getUserInterface().setProgressValue(0); Cell cell = Cell.makeInstance(destLib, cellName); if (cell == null) { Job.getUserInterface().stopProgressDialog(); return null; } // first place the instances double GAP = 5; double x = 0; double y = 0; double highest = 0; double totalSize = 0; for(Instance in : mod.instances) { Cell subCell = in.module.cell; if (subCell == null) continue; double width = subCell.getDefWidth(); double height = subCell.getDefHeight(); totalSize += (width + GAP) * (height + GAP); } double cellSize = Math.sqrt(totalSize); Map<Instance,NodeInst> placed = new HashMap<Instance,NodeInst>(); int instancesPlaced = 0; for(Instance in : mod.instances) { Cell subCell = in.module.cell; if (subCell == null) continue; double width = subCell.getDefWidth(); double height = subCell.getDefHeight(); NodeInst ni = NodeInst.makeInstance(subCell, new EPoint(x, y), width, height, cell); if (ni == null) continue; placed.put(in, ni); // cache port locations on this instance Map<PortProto,Point2D> portMap = portLocMap.get(subCell); if (portMap == null) { portMap = new HashMap<PortProto,Point2D>(); portLocMap.put(subCell, portMap); for(Iterator<PortInst> it = ni.getPortInsts(); it.hasNext(); ) { PortInst pi = it.next(); PortProto pp = pi.getPortProto(); EPoint ept = pi.getCenter(); Point2D pt = new Point2D.Double(ept.getX() - ni.getAnchorCenterX(), ept.getY() - ni.getAnchorCenterY()); portMap.put(pp, pt); } } instancesPlaced++; if ((instancesPlaced%100) == 0) Job.getUserInterface().setProgressValue(instancesPlaced * 100 / mod.instances.size()); // advance the placement x += width + GAP; highest = Math.max(highest, height); if (x >= cellSize) { x = 0; y += highest + GAP; highest = 0; } } System.out.println("Placed " + mod.instances.size() + " cell instances"); // now remove wires that do not make useful connections Netlist nl = cell.getNetlist(); for(String netName : mod.allNetworks.keySet()) { List<LocalPort> ports = mod.allNetworks.get(netName); Set<Network> used = new HashSet<Network>(); for(int i=0; i<ports.size(); i++) { LocalPort lp = ports.get(i); NodeInst ni = placed.get(lp.in); if (ni == null) { hasErrors = true; System.out.println("NodeInst " + lp.in.instanceName + " not found."); System.out.println("Check errors reported."); Job.getUserInterface().stopProgressDialog(); return null; // stop here the execution. } PortInst pi = ni.findPortInst(lp.portName); Network net = nl.getNetwork(pi); if (used.contains(net)) { ports.remove(i); i--; continue; } used.add(net); } } // now wire the instances Job.getUserInterface().setProgressNote("Creating Arcs in Cell " + cellName); Job.getUserInterface().setProgressValue(0); int total = 0; for(String netName : mod.allNetworks.keySet()) { List<LocalPort> ports = mod.allNetworks.get(netName); for(int i=1; i<ports.size(); i++) total++; } // cache information for creating unrouted arcs ArcProto ap = Generic.tech().unrouted_arc; EditingPreferences ep = cell.getEditingPreferences(); ImmutableArcInst a = ap.getDefaultInst(ep); long gridExtendOverMin = DBMath.lambdaToGrid(0.5 * ap.getDefaultLambdaBaseWidth(ep)) - ap.getGridBaseExtend(); TextDescriptor nameDescriptor = TextDescriptor.getArcTextDescriptor(); int count = 0; for(String netName : mod.allNetworks.keySet()) { List<LocalPort> ports = mod.allNetworks.get(netName); for(int i=1; i<ports.size(); i++) { LocalPort fromPort = ports.get(i-1); LocalPort toPort = ports.get(i); NodeInst fromNi = placed.get(fromPort.in); NodeInst toNi = placed.get(toPort.in); PortInst fromPi = fromNi.findPortInst(fromPort.portName); PortInst toPi = toNi.findPortInst(toPort.portName); EPoint fromCtr = getPortCenter(fromPi, portLocMap); EPoint toCtr = getPortCenter(toPi, portLocMap); ArcInst.newInstanceNoCheck(cell, ap, netName, nameDescriptor, fromPi, toPi, fromCtr, toCtr, gridExtendOverMin, ArcInst.DEFAULTANGLE, a.flags); // ArcInst.makeInstance(ap, fromPi, toPi, fromCtr, toCtr, netName); count++; if ((count%100) == 0) Job.getUserInterface().setProgressValue(count * 100 / total); netName = null; } } System.out.println("Created " + count + " wires"); Job.getUserInterface().stopProgressDialog(); return cell; } Job.getUserInterface().stopProgressDialog(); return null; } /** * Method to cache the center of ports. * This is necessary because the call to "PortInst.getCenter()" is expensive. * This method assumes that all nodes are unrotated. * @param pi the PortInst being requested. * @param portLocMap a caching map for port locations. * @return the center of the PortInst. */ private EPoint getPortCenter(PortInst pi, Map<NodeProto,Map<PortProto,Point2D>> portLocMap) { NodeInst ni = pi.getNodeInst(); Map<PortProto,Point2D> portMap = portLocMap.get(ni.getProto()); Point2D pt = portMap.get(pi.getPortProto()); return new EPoint(pt.getX() + ni.getAnchorCenterX(), pt.getY() + ni.getAnchorCenterY()); } /******************************** THE ALS NETLIST GENERATOR ********************************/ /** * Method to generate an ALS (simulation) netlist. * @param destLib destination library. * @return a List of strings with the netlist. */ public List<String> getALSNetlist(Library destLib) { // now produce the netlist if (hasErrors) return null; List<String> netlistStrings = genALS(destLib, null); return netlistStrings; } /** * Method to generate ALS target output for the created parse tree. * Assume parse tree is semantically correct. * @param destLib destination library. * @param behaveLib behavior library. * @return a list of strings that has the netlist. */ private List<String> genALS(Library destLib, Library behaveLib) { // print file header List<String> netlist = new ArrayList<String>(); netlist.add("#*************************************************"); netlist.add("# ALS Netlist file"); netlist.add("#"); if (User.isIncludeDateAndVersionInOutput()) netlist.add("# File Creation: " + TextUtils.formatDate(new Date())); netlist.add("#*************************************************"); netlist.add(""); // determine top level cell for(VModule mod : allModules) { if (mod.defined) genALSInterface(mod, netlist); } // print closing line of output file netlist.add("#********* End of netlist *******************"); return netlist; } /** * Method to generate the ALS description for the specified model. * @param module module to analyze * @param netlist the List of strings to create. */ private void genALSInterface(VModule module, List<String> netlist) { // write this entity String modLine = "model " + module.name + "("; boolean first = true; for(FormalPort fp : module.ports) { for(int i=fp.firstIndex; i<=fp.secondIndex; i++) { if (!first) modLine += ", "; first = false; modLine += fp.name; if (i != -1) modLine += "_" + i + "_"; } } modLine += ")"; netlist.add(modLine); // write instances for(Instance in : module.instances) { first = true; String inName = in.instanceName.replaceAll("/", "_").replaceAll("\\[", "_").replaceAll("\\]", "_"); String inLine = inName + ": " + in.module.name + "("; for(LocalPort lp : in.ports.keySet()) { if (!first) inLine += ", "; first = false; String name = in.ports.get(lp).replaceAll("/", "_").replaceAll("\\[", "_").replaceAll("\\]", "_"); inLine += name; } inLine += ")"; netlist.add(inLine); } netlist.add(""); } /******************************** THE QUISC NETLIST GENERATOR ********************************/ /** * Method to generate a QUISC (silicon compiler) netlist. * @param destLib destination library. * @param isIncldeDateAndVersionInOutput include date and version in output * @return a List of strings with the netlist. */ public List<String> getQUISCNetlist(Library destLib, boolean isIncludeDateAndVersionInOutput) { // now produce the netlist if (hasErrors) return null; List<String> netlist = new ArrayList<String>(); // print file header netlist.add("!*************************************************"); netlist.add("! QUISC Command file"); netlist.add("!"); if (isIncludeDateAndVersionInOutput) netlist.add("! File Creation: " + TextUtils.formatDate(new Date())); netlist.add("!-------------------------------------------------"); netlist.add(""); // determine top level cell for(VModule mod : allModules) { if (mod.defined) genQuiscInterface(mod, netlist); } // print closing line of output file netlist.add("!********* End of command file *******************"); return netlist; } /** * Method to generate the QUISC description for the specified model. * @param module module to analyze * @param netlist the List of strings to create. */ private void genQuiscInterface(VModule module, List<String> netlist) { // write this entity netlist.add("create cell " + module.name); // write instances for(Instance in : module.instances) { netlist.add("create instance " + in.instanceName + " " + in.module.name); } for(String netName : module.allNetworks.keySet()) { List<LocalPort> ports = module.allNetworks.get(netName); LocalPort last = null; for(LocalPort lp : ports) { if (last != null) netlist.add("connect " + last.in.instanceName + " " + last.portName + " " + lp.in.instanceName + " " + lp.portName); last = lp; } } // create export list for(FormalPort port : module.ports) { for(int i = port.firstIndex; i<=port.secondIndex; i++) { String name = port.name; if (i != -1) name += "[" + i + "]"; boolean found = false; for(Instance in : module.instances) { for(LocalPort lp : in.ports.keySet()) { String lpName = in.ports.get(lp); if (lpName.equals(name)) { String line = "export " + in.instanceName + " " + lp.portName + " " + name + " "; switch (port.mode) { case MODE_UNKNOWN: line += "unknown"; break; case MODE_IN: line += "input"; break; case MODE_OUT: line += "output"; break; case MODE_INOUT: line += "inout"; break; } netlist.add(line); found = true; break; } } if (found) break; } if (!found) netlist.add("! DID NOT FIND EXPORT "+name); } } } }