From 0b5250463506d86b490ea76d6547c5786d7e5f58 Mon Sep 17 00:00:00 2001
From: Jack
Date: Fri, 2 Oct 2015 14:57:46 -0700
Subject: Merged in Scala implementation of FIRRTL IR, parser, and
serialization (ie. AST -> String). Uses ANTLRv4 to generate concrete syntax
parser
---
src/main/scala/Example.scala | 19 ++++
src/main/scala/firrtl/IR.scala | 110 +++++++++++++++++++
src/main/scala/firrtl/Parser.scala | 44 ++++++++
src/main/scala/firrtl/Translator.scala | 115 +++++++++++++++++++
src/main/scala/firrtl/Utils.scala | 194 +++++++++++++++++++++++++++++++++
src/main/scala/firrtl/Visitor.scala | 185 +++++++++++++++++++++++++++++++
6 files changed, 667 insertions(+)
create mode 100644 src/main/scala/Example.scala
create mode 100644 src/main/scala/firrtl/IR.scala
create mode 100644 src/main/scala/firrtl/Parser.scala
create mode 100644 src/main/scala/firrtl/Translator.scala
create mode 100644 src/main/scala/firrtl/Utils.scala
create mode 100644 src/main/scala/firrtl/Visitor.scala
(limited to 'src/main/scala')
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