aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore15
-rw-r--r--README.md6
-rw-r--r--build.sbt14
-rw-r--r--project/sbt-antlr4.sbt3
-rw-r--r--src/main/antlr4/FIRRTL.g4207
-rw-r--r--src/main/scala/Example.scala19
-rw-r--r--src/main/scala/firrtl/IR.scala110
-rw-r--r--src/main/scala/firrtl/Parser.scala44
-rw-r--r--src/main/scala/firrtl/Translator.scala115
-rw-r--r--src/main/scala/firrtl/Utils.scala194
-rw-r--r--src/main/scala/firrtl/Visitor.scala185
11 files changed, 908 insertions, 4 deletions
diff --git a/.gitignore b/.gitignore
index ee20628e..7effd6b8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,7 +29,14 @@ spec/spec.toc
spec/spec.out
spec/spec.synctex.gz
notes/*.docx
-build.sbt
-target
-project
-src/main/scala
+
+# sbt specific
+.cache
+.history
+.lib/
+dist/*
+target/
+lib_managed/
+src_managed/
+project/boot/
+project/plugins/project/
diff --git a/README.md b/README.md
index e02f0e8e..4dc9d1b7 100644
--- a/README.md
+++ b/README.md
@@ -18,3 +18,9 @@
`make build # Build firrtl`
`make check # Run tests`
`make # Build & test`
+
+#### Scala implementation
+The Scala FIRRTL implementation relies upon sbt.
+Example use:
+ `sbt -mem 2048 "run-main Example <input> <output>`
+This command will read in &lt;input&gt; FIRRTL file, parse it, then output the AST to &lt;output&gt;
diff --git a/build.sbt b/build.sbt
new file mode 100644
index 00000000..0a083994
--- /dev/null
+++ b/build.sbt
@@ -0,0 +1,14 @@
+lazy val root = (project in file(".")).
+ settings(
+ name := "firrtl",
+ version := "1.0",
+ scalaVersion := "2.11.4"
+ )
+
+antlr4Settings
+
+antlr4GenVisitor in Antlr4 := true // default = false
+
+antlr4GenListener in Antlr4 := false // default = true
+
+antlr4PackageName in Antlr4 := Option("firrtl.antlr")
diff --git a/project/sbt-antlr4.sbt b/project/sbt-antlr4.sbt
new file mode 100644
index 00000000..df6819fa
--- /dev/null
+++ b/project/sbt-antlr4.sbt
@@ -0,0 +1,3 @@
+resolvers += "simplytyped" at "http://simplytyped.github.io/repo/releases"
+
+addSbtPlugin("com.simplytyped" % "sbt-antlr4" % "0.7.7")
diff --git a/src/main/antlr4/FIRRTL.g4 b/src/main/antlr4/FIRRTL.g4
new file mode 100644
index 00000000..08697bb1
--- /dev/null
+++ b/src/main/antlr4/FIRRTL.g4
@@ -0,0 +1,207 @@
+// Jack Koenig
+// UC Berkeley ASPIRE Lab
+// July 9, 2015
+
+grammar FIRRTL;
+
+/*------------------------------------------------------------------
+ * PARSER RULES
+ *------------------------------------------------------------------*/
+
+// TODO add [info] support (all over the place)
+// TODO Fix connect
+// TODO Add partial connect
+// TODO Should FIRRTL keywords be legal IDs?
+
+// Does there have to be at least one module?
+circuit
+ : 'circuit' id ':' '{' module* '}'
+ ;
+
+// TODO Add support for extmodule
+module
+ : 'module' id ':' '{' port* blockStmt '}'
+ ;
+
+port
+ : portKind id ':' type
+ ;
+
+portKind
+ : 'input'
+ | 'output'
+ ;
+
+type
+ : 'UInt' '<' width '>'
+ | 'SInt' '<' width '>'
+ | 'Clock'
+ | '{' field* '}' // Bundle
+ | type '[' IntLit ']' // Vector
+ ;
+
+field
+ : orientation id ':' type
+ ;
+
+// FIXME This is what the spec says it should be
+//orientation
+// : 'default'
+// | 'reverse'
+// ;
+orientation
+ : 'flip'
+ | // Nothing
+ ;
+
+width
+ : IntLit
+ | '?'
+ ;
+
+// Much faster than replacing blockStmt with stmt+
+blockStmt
+ : (stmt)*
+ ;
+
+stmt
+ : 'wire' id ':' type
+ | 'reg' id ':' type exp exp
+ | 'smem' id ':' type exp
+ | 'cmem' id ':' type exp
+ | 'inst' id (':' | 'of') id // FIXME which should it be? ':' or 'of'
+ | 'node' id '=' exp
+ | 'poison' id ':' type // Poison, FIXME
+ | dir 'accessor' id '=' exp '[' exp ']' exp? // FIXME what is this extra exp?
+ | exp ':=' exp // Connect
+ | 'onreset' exp ':=' exp
+ | exp '<>' exp // Bulk Connect
+ | exp '[' IntLit 'through' IntLit ']' ':=' exp // SubWordConnect
+ | 'when' exp ':' '{' blockStmt '}' ( 'else' ':' '{' blockStmt '}' )?
+ | 'assert' exp
+ | 'skip'
+ ;
+
+// Accessor Direction
+dir
+ : 'infer'
+ | 'read'
+ | 'write'
+ | 'rdwr'
+ ;
+
+// TODO implement
+// What is exp?
+exp
+ : 'UInt' '<' width '>' '(' (IntLit) ')' // FIXME what does "ints" mean?
+ | 'SInt' '<' width '>' '(' (IntLit) ')' // FIXME same
+ | id // Ref
+ | exp '.' id // FIXME Does this work for no space?
+ | exp '[' IntLit ']'
+ | primop '(' exp* IntLit* ')' // FIXME Need a big check here
+ ;
+
+id
+ : Id
+ | keyword
+ ;
+
+// FIXME need to make sure this is exhaustive including all FIRRTL keywords that are legal IDs
+keyword
+ : primop
+ | dir
+ | 'inst'
+ ;
+
+primop
+ : 'add'
+ | 'sub'
+ | 'addw'
+ | 'subw'
+ | 'mul'
+ | 'div'
+ | 'mod'
+ | 'quo'
+ | 'rem'
+ | 'lt'
+ | 'leq'
+ | 'gt'
+ | 'geq'
+ | 'eq'
+ | 'neq'
+ | 'mux'
+ | 'pad'
+ | 'asUInt'
+ | 'asSInt'
+ | 'shl'
+ | 'shr'
+ | 'dshl'
+ | 'dshr'
+ | 'cvt'
+ | 'neg'
+ | 'not'
+ | 'and'
+ | 'or'
+ | 'xor'
+ | 'andr'
+ | 'orr'
+ | 'xorr'
+ | 'cat'
+ | 'bit'
+ | 'bits'
+ ;
+
+/*------------------------------------------------------------------
+ * LEXER RULES
+ *------------------------------------------------------------------*/
+
+Id
+ : IdNondigit
+ ( IdNondigit
+ | Digit
+ )*
+ ;
+
+fragment
+IdNondigit
+ : Nondigit
+ | [~!@#$%^*-+=?/]
+ ;
+
+// Should enforcing signed, non-neg, and positive ints be done in parser?
+// => YES
+IntLit
+ : '0'
+ | ( '+' | '-' )? [1-9] ( Digit )*
+ | '"' 'h' ( HexDigit )+ '"'
+ ;
+
+fragment
+Nondigit
+ : [a-zA-Z_]
+ ;
+
+fragment
+Digit
+ : [0-9]
+ ;
+
+fragment
+HexDigit
+ : [a-zA-Z0-9]
+ ;
+
+Comment
+ : ';' ~[\r\n]*
+ -> skip
+ ;
+
+Whitespace
+ : [ \t,]+
+ -> skip
+ ;
+
+Newline
+ : ( '\r'? '\n' )+
+ -> skip
+ ;
diff --git a/src/main/scala/Example.scala b/src/main/scala/Example.scala
new file mode 100644
index 00000000..210a50fb
--- /dev/null
+++ b/src/main/scala/Example.scala
@@ -0,0 +1,19 @@
+import java.io._
+import firrtl._
+import firrtl.Utils._
+
+object Example
+{
+ // Example use of Scala FIRRTL parser and serialization
+ def main(args: Array[String])
+ {
+ val inputFile = args(0)
+
+ // Parse file
+ val ast = firrtl.Parser.parse(inputFile)
+
+ val writer = new PrintWriter(new File(args(1)))
+ writer.write(ast.serialize) // serialize returns String
+ writer.close()
+ }
+}
diff --git a/src/main/scala/firrtl/IR.scala b/src/main/scala/firrtl/IR.scala
new file mode 100644
index 00000000..c52c45a4
--- /dev/null
+++ b/src/main/scala/firrtl/IR.scala
@@ -0,0 +1,110 @@
+package firrtl
+
+import scala.collection.Seq
+
+// Should this be defined elsewhere?
+case class FileInfo(file: String, line: Int, column: Int) {
+ override def toString(): String = s"$file@$line.$column"
+}
+
+trait AST
+
+trait PrimOp extends AST
+case object Add extends PrimOp
+case object Sub extends PrimOp
+case object Addw extends PrimOp
+case object Subw extends PrimOp
+case object Mul extends PrimOp
+case object Div extends PrimOp
+case object Mod extends PrimOp
+case object Quo extends PrimOp
+case object Rem extends PrimOp
+case object Lt extends PrimOp
+case object Leq extends PrimOp
+case object Gt extends PrimOp
+case object Geq extends PrimOp
+case object Eq extends PrimOp
+case object Neq extends PrimOp
+case object Mux extends PrimOp
+case object Pad extends PrimOp
+case object AsUInt extends PrimOp
+case object AsSInt extends PrimOp
+case object Shl extends PrimOp
+case object Shr extends PrimOp
+case object Dshl extends PrimOp
+case object Dshr extends PrimOp
+case object Cvt extends PrimOp
+case object Neg extends PrimOp
+case object Not extends PrimOp
+case object And extends PrimOp
+case object Or extends PrimOp
+case object Xor extends PrimOp
+case object Andr extends PrimOp
+case object Orr extends PrimOp
+case object Xorr extends PrimOp
+case object Cat extends PrimOp
+case object Bit extends PrimOp
+case object Bits extends PrimOp
+
+// TODO stanza ir has types on many of these, why? Is it the type of what we're referencing?
+// Add types, default to UNKNOWN
+// TODO add type
+trait Exp extends AST
+case class UIntValue(value: BigInt, width: BigInt) extends Exp
+case class SIntValue(value: BigInt, width: BigInt) extends Exp
+case class Ref(name: String, tpe: Type) extends Exp
+case class Subfield(exp: Exp, name: String, tpe: Type) extends Exp
+case class Subindex(exp: Exp, value: BigInt) extends Exp
+case class DoPrimOp(op: PrimOp, args: Seq[Exp], consts: Seq[BigInt]) extends Exp
+
+trait AccessorDir extends AST
+case object Infer extends AccessorDir
+case object Read extends AccessorDir
+case object Write extends AccessorDir
+case object RdWr extends AccessorDir
+
+trait Stmt extends AST
+case class DefWire(info: FileInfo, name: String, tpe: Type) extends Stmt
+case class DefReg(info: FileInfo, name: String, tpe: Type, clock: Exp, reset: Exp) extends Stmt
+case class DefMemory(info: FileInfo, name: String, seq: Boolean, tpe: Type, clock: Exp) extends Stmt
+case class DefInst(info: FileInfo, name: String, module: Exp) extends Stmt
+case class DefNode(info: FileInfo, name: String, value: Exp) extends Stmt
+case class DefPoison(info: FileInfo, name: String, tpe: Type) extends Stmt
+case class DefAccessor(info: FileInfo, name: String, dir: AccessorDir, source: Exp, index: Exp) extends Stmt
+case class OnReset(info: FileInfo, lhs: Exp, rhs: Exp) extends Stmt
+case class Connect(info: FileInfo, lhs: Exp, rhs: Exp) extends Stmt
+case class BulkConnect(info: FileInfo, lhs: Exp, rhs: Exp) extends Stmt
+case class When(info: FileInfo, pred: Exp, conseq: Stmt, alt: Stmt) extends Stmt
+case class Assert(info: FileInfo, pred: Exp) extends Stmt
+case class Block(stmts: Seq[Stmt]) extends Stmt
+case object EmptyStmt extends Stmt
+
+trait Width extends AST
+case class IntWidth(width: BigInt) extends Width
+case object UnknownWidth extends Width
+
+trait FieldDir extends AST
+case object Default extends FieldDir
+case object Reverse extends FieldDir
+
+case class Field(name: String, dir: FieldDir, tpe: Type) extends AST
+
+trait Type extends AST
+case class UIntType(width: Width) extends Type
+case class SIntType(width: Width) extends Type
+case object ClockType extends Type
+case class BundleType(fields: Seq[Field]) extends Type
+case class VectorType(tpe: Type, size: BigInt) extends Type
+case object UnknownType extends Type
+
+trait PortDir extends AST
+case object Input extends PortDir
+case object Output extends PortDir
+
+case class Port(info: FileInfo, name: String, dir: PortDir, tpe: Type) extends AST
+
+case class Module(info: FileInfo, name: String, ports: Seq[Port], stmt: Stmt) extends AST
+
+case class Circuit(info: FileInfo, name: String, modules: Seq[Module]) extends AST
+
+
diff --git a/src/main/scala/firrtl/Parser.scala b/src/main/scala/firrtl/Parser.scala
new file mode 100644
index 00000000..35e41222
--- /dev/null
+++ b/src/main/scala/firrtl/Parser.scala
@@ -0,0 +1,44 @@
+package firrtl
+
+import org.antlr.v4.runtime._;
+import org.antlr.v4.runtime.atn._;
+import org.antlr.v4.runtime.tree._;
+import java.io.FileInputStream
+import scala.collection.JavaConverters._
+import scala.io.Source
+import Utils._
+import antlr._
+
+object Parser
+{
+
+ /** Takes a firrtl filename, returns AST (root node is Circuit)
+ *
+ * Currently must be standard FIRRTL file
+ * Parser performs conversion to machine firrtl
+ */
+ def parse(filename: String): Circuit = {
+ //val antlrStream = new ANTLRInputStream(input.reader)
+ val fixedInput = Translator.addBrackets(Source.fromFile(filename).getLines)
+ val antlrStream = new ANTLRInputStream(fixedInput.result)
+ val lexer = new FIRRTLLexer(antlrStream)
+ val tokens = new CommonTokenStream(lexer)
+ val parser = new FIRRTLParser(tokens)
+
+ // FIXME Dangerous
+ parser.getInterpreter.setPredictionMode(PredictionMode.SLL)
+
+ // Concrete Syntax Tree
+ val cst = parser.circuit
+
+ val visitor = new Visitor(filename)
+ //val ast = visitor.visitCircuit(cst) match {
+ val ast = visitor.visit(cst) match {
+ case c: Circuit => c
+ case x => throw new ClassCastException("Error! AST not rooted with Circuit node!")
+ }
+
+ ast
+ }
+
+}
diff --git a/src/main/scala/firrtl/Translator.scala b/src/main/scala/firrtl/Translator.scala
new file mode 100644
index 00000000..eba78b39
--- /dev/null
+++ b/src/main/scala/firrtl/Translator.scala
@@ -0,0 +1,115 @@
+
+/* TODO
+ * - Add support for comments (that being said, current Scopers regex should ignore commented lines)
+ * - Add better error messages for illformed FIRRTL
+ * - Add support for files that do not have a circuit (like a module by itself in a file)
+ * - Improve performance? Replace regex?
+ * - Add proper commnad-line arguments?
+ * - Wrap in Reader subclass. This would have less memory footprint than creating a large string
+ */
+
+package firrtl
+
+import scala.io.Source
+import scala.collection.mutable.Stack
+import scala.collection.mutable.StringBuilder
+import java.io._
+
+
+object Translator
+{
+
+ def addBrackets(inputIt: Iterator[String]): StringBuilder = {
+ def countSpaces(s: String): Int = s.prefixLength(_ == ' ')
+
+ val Scopers = """\s*(circuit|module|when|else)(.*)""".r
+
+ // Function start
+ val it = inputIt.zipWithIndex
+ var ret = new StringBuilder()
+
+ if( !it.hasNext ) throw new Exception("Empty file!")
+
+ // Find circuit before starting scope checks
+ var line = it.next
+ while ( it.hasNext && !line._1.contains("circuit") ) {
+ ret ++= line._1 + "\n"
+ line = it.next
+ }
+ ret ++= line._1 + " { \n"
+ if( !it.hasNext ) throw new Exception("No circuit in file!")
+
+
+ val scope = Stack[Int]()
+ scope.push(countSpaces(line._1))
+ var newScope = true // indicates if increasing scope spacing is legal on next line
+
+ while( it.hasNext ) {
+ it.next match { case (text, lineNum) =>
+ val spaces = countSpaces(text)
+
+ val l = if (text.length > spaces ) { // Check that line has text in it
+ if (newScope) {
+ scope.push(countSpaces(text))
+ } else {
+
+ // Check if change in current scope
+ if( spaces < scope.top ) {
+ while( spaces < scope.top ) {
+ // Close scopes (adding brackets as we go)
+ scope.pop()
+ ret.deleteCharAt(ret.lastIndexOf("\n")) // Put on previous line
+ ret ++= " }\n"
+ }
+ if( spaces != scope.top )
+ throw new Exception("Spacing does not match scope on line : " + lineNum + " : " + scope.top)
+ }
+ else if( spaces > scope.top )
+ throw new Exception("Invalid increase in scope on line " + lineNum)
+ }
+ // Now match on legal scope increasers
+ text match {
+ case Scopers(keyword, _* ) => {
+ newScope = true
+ text + " { "
+ }
+ case _ => {
+ newScope = false
+ text
+ }
+ }
+ } // if( text.length > spaces )
+ else {
+ text // empty lines
+ }
+
+ ret ++= l + "\n"
+ } // it.next match
+ } // while( it.hasNext )
+
+ // Print any closing braces
+ while( scope.top > 0 ) {
+ scope.pop()
+ ret.deleteCharAt(ret.lastIndexOf("\n")) // Put on previous line
+ ret ++= " }\n"
+ }
+
+ ret
+ }
+
+ def main(args: Array[String]) {
+
+ try {
+ val translation = addBrackets(Source.fromFile(args(0)).getLines)
+
+ val writer = new PrintWriter(new File(args(1)))
+ writer.write(translation.result)
+ writer.close()
+ } catch {
+ case e: Exception => {
+ throw new Exception("USAGE: Translator <input file> <output file>\n" + e)
+ }
+ }
+ }
+
+}
diff --git a/src/main/scala/firrtl/Utils.scala b/src/main/scala/firrtl/Utils.scala
new file mode 100644
index 00000000..916408bc
--- /dev/null
+++ b/src/main/scala/firrtl/Utils.scala
@@ -0,0 +1,194 @@
+// Utility functions for FIRRTL IR
+
+/* TODO
+ * - Adopt style more similar to Chisel3 Emitter?
+ */
+
+package firrtl
+
+import scala.collection.mutable.StringBuilder
+
+object Utils {
+
+ implicit class BigIntUtils(bi: BigInt){
+ def serialize(): String =
+ "\"h0" + bi.toString(16) + "\""
+ }
+
+ implicit class PrimOpUtils(op: PrimOp) {
+ def serialize(): String = {
+ op match {
+ case Add => "add"
+ case Sub => "sub"
+ case Addw => "addw"
+ case Subw => "subw"
+ case Mul => "mul"
+ case Div => "div"
+ case Mod => "mod"
+ case Quo => "quo"
+ case Rem => "rem"
+ case Lt => "lt"
+ case Leq => "leq"
+ case Gt => "gt"
+ case Geq => "geq"
+ case Eq => "eq"
+ case Neq => "neq"
+ case Mux => "mux"
+ case Pad => "pad"
+ case AsUInt => "asUInt"
+ case AsSInt => "asSInt"
+ case Shl => "shl"
+ case Shr => "shr"
+ case Dshl => "dshl"
+ case Dshr => "dshr"
+ case Cvt => "cvt"
+ case Neg => "neg"
+ case Not => "not"
+ case And => "and"
+ case Or => "or"
+ case Xor => "xor"
+ case Andr => "andr"
+ case Orr => "orr"
+ case Xorr => "xorr"
+ case Cat => "cat"
+ case Bit => "bit"
+ case Bits => "bits"
+ }
+ }
+ }
+
+ implicit class ExpUtils(exp: Exp) {
+ def serialize(): String =
+ exp match {
+ case v: UIntValue => s"UInt<${v.width}>(${v.value.serialize})"
+ case v: SIntValue => s"SInt<${v.width}>(${v.value.serialize})"
+ case r: Ref => r.name
+ case s: Subfield => s"${s.exp.serialize}.${s.name}"
+ case s: Subindex => s"${s.exp.serialize}[${s.value}]"
+ case p: DoPrimOp =>
+ s"${p.op.serialize}(" + (p.args.map(_.serialize) ++ p.consts.map(_.toString)).mkString(", ") + ")"
+ }
+ }
+
+ // AccessorDir
+ implicit class AccessorDirUtils(dir: AccessorDir) {
+ def serialize(): String =
+ dir match {
+ case Infer => "infer"
+ case Read => "read"
+ case Write => "write"
+ case RdWr => "rdwr"
+ }
+ }
+
+
+ implicit class StmtUtils(stmt: Stmt) {
+ def serialize(): String =
+ stmt match {
+ case w: DefWire => s"wire ${w.name} : ${w.tpe.serialize}"
+ case r: DefReg => s"reg ${r.name} : ${r.tpe.serialize}, ${r.clock.serialize}, ${r.reset.serialize}"
+ case m: DefMemory => (if(m.seq) "smem" else "cmem") +
+ s" ${m.name} : ${m.tpe.serialize}, ${m.clock.serialize}"
+ case i: DefInst => s"inst ${i.name} of ${i.module.serialize}"
+ case n: DefNode => s"node ${n.name} = ${n.value.serialize}"
+ case p: DefPoison => s"poison ${p.name} : ${p.tpe.serialize}"
+ case a: DefAccessor => s"${a.dir.serialize} accessor ${a.name} = ${a.source.serialize}[${a.index.serialize}]"
+ case c: Connect => s"${c.lhs.serialize} := ${c.rhs.serialize}"
+ case o: OnReset => s"onreset ${o.lhs.serialize} := ${o.rhs.serialize}"
+ case b: BulkConnect => s"${b.lhs.serialize} <> ${b.rhs.serialize}"
+ case w: When => {
+ var str = new StringBuilder(s"when ${w.pred.serialize} : ")
+ withIndent { str ++= w.conseq.serialize }
+ if( w.alt != EmptyStmt ) {
+ str ++= newline + "else :"
+ withIndent { str ++= w.alt.serialize }
+ }
+ str.result
+ }
+ //case b: Block => b.stmts.map(newline ++ _.serialize).mkString
+ case b: Block => {
+ val s = new StringBuilder
+ b.stmts.foreach { s ++= newline ++ _.serialize }
+ s.result
+ }
+ case a: Assert => s"assert ${a.pred.serialize}"
+ case EmptyStmt => "skip"
+ }
+ }
+
+ implicit class WidthUtils(w: Width) {
+ def serialize(): String =
+ w match {
+ case UnknownWidth => "?"
+ case w: IntWidth => w.width.toString
+ }
+ }
+
+ implicit class FieldDirUtils(dir: FieldDir) {
+ def serialize(): String =
+ dir match {
+ case Reverse => "flip "
+ case Default => ""
+ }
+ }
+
+ implicit class FieldUtils(field: Field) {
+ def serialize(): String =
+ s"${field.dir.serialize} ${field.name} : ${field.tpe.serialize}"
+ }
+
+ implicit class TypeUtils(t: Type) {
+ def serialize(): String = {
+ val commas = ", " // for mkString in BundleType
+ t match {
+ case ClockType => "Clock"
+ case UnknownType => "UnknownType"
+ case t: UIntType => s"UInt<${t.width.serialize}>"
+ case t: SIntType => s"SInt<${t.width.serialize}>"
+ case t: BundleType => s"{ ${t.fields.map(_.serialize).mkString(commas)} }"
+ case t: VectorType => s"${t.tpe.serialize}[${t.size}]"
+ }
+ }
+ }
+
+ implicit class PortDirUtils(p: PortDir) {
+ def serialize(): String =
+ p match {
+ case Input => "input"
+ case Output => "output"
+ }
+ }
+
+ implicit class PortUtils(p: Port) {
+ def serialize(): String =
+ s"${p.dir.serialize} ${p.name} : ${p.tpe.serialize}"
+ }
+
+ implicit class ModuleUtils(m: Module) {
+ def serialize(): String = {
+ var s = new StringBuilder(s"module ${m.name} : ")
+ withIndent {
+ s ++= m.ports.map(newline ++ _.serialize).mkString
+ s ++= newline ++ m.stmt.serialize
+ }
+ s.toString
+ }
+ }
+
+ implicit class CircuitUtils(c: Circuit) {
+ def serialize(): String = {
+ var s = new StringBuilder(s"circuit ${c.name} : ")
+ //withIndent { c.modules.foreach(s ++= newline ++ newline ++ _.serialize) }
+ withIndent { s ++= newline ++ c.modules.map(_.serialize).mkString(newline + newline) }
+ s ++= newline ++ newline
+ s.toString
+ }
+ }
+
+ private var indentLevel = 0
+ private def newline = "\n" + (" " * indentLevel)
+ private def indent(): Unit = indentLevel += 1
+ private def unindent() { require(indentLevel > 0); indentLevel -= 1 }
+ private def withIndent(f: => Unit) { indent(); f; unindent() }
+
+}
diff --git a/src/main/scala/firrtl/Visitor.scala b/src/main/scala/firrtl/Visitor.scala
new file mode 100644
index 00000000..276facce
--- /dev/null
+++ b/src/main/scala/firrtl/Visitor.scala
@@ -0,0 +1,185 @@
+/*
+ * TODO FIXME
+ * - Support all integer types (not just "h...")
+ * - In ANTLR examples they use just visit, why am I having to use visitModule or other specific functions?
+ * - Make visit private?
+*/
+
+package firrtl
+
+import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor;
+import org.antlr.v4.runtime.ParserRuleContext
+import org.antlr.v4.runtime.tree.ErrorNode
+import org.antlr.v4.runtime.tree.TerminalNode
+import scala.collection.JavaConversions._
+import antlr._
+
+class Visitor(val fullFilename: String) extends FIRRTLBaseVisitor[AST]
+{
+ // Strip file path
+ private val filename = fullFilename.drop(fullFilename.lastIndexOf("/")+1)
+
+ // For some reason visitCircuit does not visit the right function
+ // FIXME for some reason this cannot be private, probably because it extends
+ // FIRRTLBaseVisitor which is in a subpackage?
+ def visit[AST](ctx: FIRRTLParser.CircuitContext): Circuit = visitCircuit(ctx)
+
+ // These regex have to change if grammar changes
+ private def string2BigInt(s: String): BigInt = {
+ // private define legal patterns
+ val HexPattern = """\"*h([a-zA-Z0-9]+)\"*""".r
+ val DecPattern = """(\+|-)?([1-9]\d*)""".r
+ val ZeroPattern = "0".r
+ s match {
+ case ZeroPattern(_*) => BigInt(0)
+ case HexPattern(hexdigits) => BigInt(hexdigits, 16)
+ case DecPattern(sign, num) => BigInt(num)
+ case _ => throw new Exception("Invalid String for conversion to BigInt " + s)
+ }
+ }
+ private def getFileInfo(ctx: ParserRuleContext): FileInfo =
+ FileInfo(filename, ctx.getStart().getLine(), ctx.getStart().getCharPositionInLine())
+
+ private def visitCircuit[AST](ctx: FIRRTLParser.CircuitContext): Circuit =
+ Circuit(getFileInfo(ctx), ctx.id.getText, ctx.module.map(visitModule))
+
+ private def visitModule[AST](ctx: FIRRTLParser.ModuleContext): Module =
+ Module(getFileInfo(ctx), ctx.id.getText, ctx.port.map(visitPort), visitBlockStmt(ctx.blockStmt))
+
+ private def visitPort[AST](ctx: FIRRTLParser.PortContext): Port =
+ Port(getFileInfo(ctx), ctx.id.getText, visitPortKind(ctx.portKind), visitType(ctx.`type`))
+
+ private def visitPortKind[AST](ctx: FIRRTLParser.PortKindContext): PortDir =
+ ctx.getText match {
+ case "input" => Input
+ case "output" => Output
+ }
+
+ // Match on a type instead of on strings?
+ private def visitType[AST](ctx: FIRRTLParser.TypeContext): Type = {
+ ctx.getChild(0).getText match {
+ case "UInt" => UIntType( visitWidth(ctx.width) )
+ case "SInt" => SIntType( visitWidth(ctx.width) )
+ case "Clock" => ClockType
+ case "{" => BundleType(ctx.field.map(visitField))
+ case _ => new VectorType( visitType(ctx.`type`), string2BigInt(ctx.IntLit.getText) )
+ }
+ }
+
+ private def visitField[AST](ctx: FIRRTLParser.FieldContext): Field =
+ Field(ctx.id.getText, visitOrientation(ctx.orientation), visitType(ctx.`type`))
+
+ private def visitOrientation[AST](ctx: FIRRTLParser.OrientationContext): FieldDir =
+ ctx.getText match {
+ case "flip" => Reverse
+ case _ => Default
+ }
+
+ private def visitWidth[AST](ctx: FIRRTLParser.WidthContext): Width = {
+ val text = ctx.getText
+ text match {
+ case "?" => UnknownWidth
+ case _ => IntWidth(string2BigInt(text))
+ }
+ }
+
+ private def visitBlockStmt[AST](ctx: FIRRTLParser.BlockStmtContext): Stmt =
+ Block(ctx.stmt.map(visitStmt))
+
+ private def visitStmt[AST](ctx: FIRRTLParser.StmtContext): Stmt = {
+ val info = getFileInfo(ctx)
+
+ ctx.getChild(0).getText match {
+ case "wire" => DefWire(info, ctx.id(0).getText, visitType(ctx.`type`))
+ case "reg" => DefReg(info, ctx.id(0).getText, visitType(ctx.`type`),
+ visitExp(ctx.exp(0)), visitExp(ctx.exp(1)))
+ case "smem" => DefMemory(info, ctx.id(0).getText, true,
+ visitType(ctx.`type`), visitExp(ctx.exp(0)))
+ case "cmem" => DefMemory(info, ctx.id(0).getText, false,
+ visitType(ctx.`type`), visitExp(ctx.exp(0)))
+ case "inst" => DefInst(info, ctx.id(0).getText, Ref(ctx.id(1).getText, UnknownType))
+ case "node" => DefNode(info, ctx.id(0).getText, visitExp(ctx.exp(0)))
+ case "poison" => DefPoison(info, ctx.id(0).getText, visitType(ctx.`type`))
+ case "onreset" => OnReset(info, visitExp(ctx.exp(0)), visitExp(ctx.exp(1)))
+ case "when" => {
+ val alt = if (ctx.blockStmt.length > 1) visitBlockStmt(ctx.blockStmt(1)) else EmptyStmt
+ When(info, visitExp(ctx.exp(0)), visitBlockStmt(ctx.blockStmt(0)), alt)
+ }
+ case "assert" => Assert(info, visitExp(ctx.exp(0)))
+ case "skip" => EmptyStmt
+ // If we don't match on the first child, try the next one
+ case _ => {
+ ctx.getChild(1).getText match {
+ case "accessor" => DefAccessor(info, ctx.id(0).getText,
+ visitDir(ctx.dir), visitExp(ctx.exp(0)), visitExp(ctx.exp(1)))
+ case ":=" => Connect(info, visitExp(ctx.exp(0)), visitExp(ctx.exp(1)) )
+ case "<>" => BulkConnect(info, visitExp(ctx.exp(0)), visitExp(ctx.exp(1)) )
+ }
+ }
+ }
+ }
+
+ private def visitDir[AST](ctx: FIRRTLParser.DirContext): AccessorDir =
+ ctx.getText match {
+ case "infer" => Infer
+ case "read" => Read
+ case "write" => Write
+ case "rdwr" => RdWr // TODO remove exceptions for unmatched things
+ }
+
+ private def visitExp[AST](ctx: FIRRTLParser.ExpContext): Exp =
+ if( ctx.getChildCount == 1 )
+ Ref(ctx.getText, UnknownType)
+ else
+ ctx.getChild(0).getText match {
+ case "UInt" => UIntValue(string2BigInt(ctx.IntLit(0).getText), string2BigInt(ctx.width.getText))
+ case "SInt" => SIntValue(string2BigInt(ctx.IntLit(0).getText), string2BigInt(ctx.width.getText))
+ case _ =>
+ ctx.getChild(1).getText match {
+ case "." => new Subfield(visitExp(ctx.exp(0)), ctx.id.getText, UnknownType)
+ case "[" => new Subindex(visitExp(ctx.exp(0)), string2BigInt(ctx.IntLit(0).getText))
+ case "(" =>
+ DoPrimOp(visitPrimop(ctx.primop), ctx.exp.map(visitExp), ctx.IntLit.map(x => string2BigInt(x.getText)))
+ }
+ }
+
+ // TODO can I create this and have the opposite? create map and invert it?
+ private def visitPrimop[AST](ctx: FIRRTLParser.PrimopContext): PrimOp =
+ ctx.getText match {
+ case "add" => Add
+ case "sub" => Sub
+ case "addw" => Addw
+ case "subw" => Subw
+ case "mul" => Mul
+ case "div" => Div
+ case "mod" => Mod
+ case "quo" => Quo
+ case "rem" => Rem
+ case "lt" => Lt
+ case "leq" => Leq
+ case "gt" => Gt
+ case "geq" => Geq
+ case "eq" => Eq
+ case "neq" => Neq
+ case "mux" => Mux
+ case "pad" => Pad
+ case "asUInt" => AsUInt
+ case "asSInt" => AsSInt
+ case "shl" => Shl
+ case "shr" => Shr
+ case "dshl" => Dshl
+ case "dshr" => Dshr
+ case "cvt" => Cvt
+ case "neg" => Neg
+ case "not" => Not
+ case "and" => And
+ case "or" => Or
+ case "xor" => Xor
+ case "andr" => Andr
+ case "orr" => Orr
+ case "xorr" => Xorr
+ case "cat" => Cat
+ case "bit" => Bit
+ case "bits" => Bits
+ }
+}