/* -*- tab-width: 4 -*- * * Electric(tm) VLSI Design System * * File: VerilogParser.java * Written by Jonathan Gainsley, Sun Microsystems. * * 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.simulation.test; import java.util.ArrayList; import java.util.List; import java.util.Iterator; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.io.BufferedReader; import java.io.FileReader; import java.io.StreamTokenizer; import java.io.IOException; /** * A brain-dead, extremely limited verilog parser. In fact, it really * doesn't do anything except return a module name with it's port defintions. * * <P>Again, this is not meant to be a fully featured parser. It's error handling * is also not very useful. It is only meant to extract module names and their ports. */ public class VerilogParser { /** * A Verilog Module. Contains a list of Ports. Does not * contain a list of sub-modules, always statements, or anything else! */ public static final class Module { public final String name; private final List inports; private final List outports; private final List inoutports; public Module(String name) { this.name = name; inports = new ArrayList(); outports = new ArrayList(); inoutports = new ArrayList(); } void addPort(Port port) { if (port.type == Port.INPUT) inports.add(port); else if (port.type == Port.OUTPUT) outports.add(port); else if (port.type == Port.INOUT) inoutports.add(port); else { System.out.println("Unknown port type ("+port.type+") for port "+port.name); } } List getInports() { return inports; } List getOutports() { return outports; } List getInoutports() { return inoutports; } List getPorts() { List ports = new ArrayList(); ports.addAll(inports); ports.addAll(outports); ports.addAll(inoutports); return ports; } StringBuffer print() { StringBuffer buf = new StringBuffer(); buf.append("module "+name); buf.append(printPorts(null)); return buf; } StringBuffer printPorts(VerilogModel.AllAliasedNames aliased) { StringBuffer buf = new StringBuffer(); int lineno = 0; buf.append("("); for (Iterator it = getPorts().iterator(); it.hasNext(); ) { Port p = (Port)it.next(); String alias = p.name; if (aliased != null) alias = aliased.getAliasFor(alias); buf.append("."+p.name+"("+alias+")"); if (it.hasNext()) buf.append(", "); if ((int)(buf.length() / 60) > lineno) { buf.append("\n\t"); lineno++; } } buf.append(");\n"); return buf; } } /** * A Verilog port. May be bussed. */ public static final class Port { public final String name; public final int type; public final int start; public final int end; public final boolean containsSpecialChars; public static final int INPUT = 0; public static final int OUTPUT = 1; public static final int INOUT = 2; private static final Pattern bussed = Pattern.compile("(\\w+)\\[(\\d+):(\\d+)\\]"); private static final Pattern bussedSig = Pattern.compile("(\\w+)\\[(\\d+)\\]"); public Port(String name, int type, int start, int end, boolean containsSpecialChars) { this.name = name; this.type = type; this.start = start; this.end = end; this.containsSpecialChars = containsSpecialChars; //System.out.println("Created port "+name+", "+type+", "+start+", "+end); } public Port(String name, int type) { this(name, type, 1, 1, false); } public boolean contains(String name) { if (name.equals(this.name)) return true; Matcher m = bussedSig.matcher(name); if (start != end && m.matches()) { String n = m.group(1); int idx = Integer.parseInt(m.group(2)); if (n.equals(this.name) && (idx >= start && idx <= end)) return true; } return false; } } private List modules; private BufferedReader fin; public VerilogParser() { modules = new ArrayList(); fin = null; } public List getModules() { return modules; } /** * Parse a verilog file. Very limited functionality at this time. * Only builds list of modules and their ports * @param verilogFile * @return list of modules parsed */ public boolean parse(String verilogFile) { // try to open the file try { FileReader reader = new FileReader(verilogFile); fin = new BufferedReader(reader); } catch (java.io.IOException e) { System.out.println("Failed to open file for read: "+verilogFile+": "+e.getMessage()); } if (fin == null) return false; StreamTokenizer st = new StreamTokenizer(fin); st.slashSlashComments(true); st.slashStarComments(true); st.wordChars('_', '_'); try { int token = st.nextToken(); while (token != StreamTokenizer.TT_EOF) { switch(token) { case StreamTokenizer.TT_WORD: { // Parse Modules if (st.sval.equals("module")) { if (!parseModule(st)) return false; } else if (st.sval.equals("primitive")) { if (!parsePrimitive(st)) return false; } else { error(st, "expected module/primitive definition"); } break; } case '`': { int t = st.nextToken(); if (st.sval.equals("include")) { st.nextToken(); // grab file name } else if (st.sval.equals("define")) { st.nextToken(); st.nextToken(); } else { error(st, "expected valid defintion after `"); } break; } default: { error(st, "expected word"); } } token = st.nextToken(); } } catch (java.io.IOException e) { System.out.println(e.getMessage()); return false; } return true; } private boolean parseModule(StreamTokenizer st) { try { int token = st.nextToken(); if (token != StreamTokenizer.TT_WORD) { error(st, "expected module name"); return false; } Module module = new Module(st.sval); expect(st, '('); while (token != ')') { // gobble ports in module definition token = st.nextToken(); } expect(st, ';'); // parse ports boolean processPorts = true; while (processPorts) { token = st.nextToken(); switch(token) { case StreamTokenizer.TT_WORD: { if (st.sval.equalsIgnoreCase("input")) { parsePorts(st, Port.INPUT, module); } else if (st.sval.equalsIgnoreCase("output")) { parsePorts(st, Port.OUTPUT, module); } else if (st.sval.equalsIgnoreCase("inout")) { parsePorts(st, Port.INOUT, module); } else { processPorts = false; st.pushBack(); } break; } } } // eat rest of module while(true) { token = st.nextToken(); if (token == StreamTokenizer.TT_EOF) break; if (token == StreamTokenizer.TT_WORD && st.sval.equals("endmodule")) break; } modules.add(module); } catch (java.io.IOException e) { System.out.println(e.getMessage()); return false; } return true; } private boolean parsePrimitive(StreamTokenizer st) { try { while (true) { int token = st.nextToken(); if (token == StreamTokenizer.TT_EOF) break; if (token == StreamTokenizer.TT_WORD && st.sval.equals("endprimitive")) break; } } catch (IOException e) { System.out.println(e.getMessage()); return false; } return true; } private void parsePorts(StreamTokenizer st, int type, Module module) { try { int token; while ( (token = st.nextToken()) != ';') { if (token == ',') continue; String name = st.sval; int start = 1, end = 1; boolean quoted = false; if (token == '[') { // parse bus st.nextToken(); start = (int)st.nval; expect(st, ':'); st.nextToken(); end = (int)st.nval; expect(st, ']'); token = st.nextToken(); name = st.sval; } if (token == '\\') { // parse special string delimited by \ at start and single whitespace at end StringBuffer buf = new StringBuffer("\\"); st.ordinaryChar(' '); while ( (token = st.nextToken()) != ' ') { if (token == StreamTokenizer.TT_WORD) buf.append(st.sval); else if (token == StreamTokenizer.TT_NUMBER) buf.append((int)st.nval); else buf.append((char)token); } st.whitespaceChars(' ', ' '); buf.append(" "); name = buf.toString(); quoted = true; } module.addPort(new Port(name, type, start, end, quoted)); } } catch (java.io.IOException e) { System.out.println(e.getMessage()); } } private boolean expect(StreamTokenizer st, int nextToken) { try { int token = st.nextToken(); if (token != nextToken) { error(st, "expected "+(char)nextToken); return false; } } catch (java.io.IOException e) { System.out.println(e.getMessage()); return false; } return true; } private void error(StreamTokenizer st, String msg) { System.out.print("Parse error on token "); switch(st.ttype) { case StreamTokenizer.TT_WORD: System.out.print("\""+st.sval+"\""); case StreamTokenizer.TT_NUMBER: System.out.print("\""+st.nval+"\""); case StreamTokenizer.TT_EOF: System.out.print("EOF"); case StreamTokenizer.TT_EOL: System.out.print("EOL"); default: System.out.print("\""+(char)st.ttype+"\""); } System.out.println(", line "+st.lineno()+": "+msg); } /** unit test */ public static void main(String args[]) { VerilogParser vp = new VerilogParser(); if (!vp.parse(VerilogModel.getExampleVerilogChipFile())) { System.out.println("Parsing failed."); return; } System.out.println("Parsing succeeded: modules are:"); for (Iterator it = vp.getModules().iterator(); it.hasNext(); ) { Module m = (Module)it.next(); System.out.println(m.print()); } } }