aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZachary Yedidia2022-08-26 07:52:04 -0700
committerGitHub2022-08-26 14:52:04 +0000
commit19fe90bb0fd37457c47f3873392db5cbb9b87d38 (patch)
tree8d81c6b3d0ddf3b050a021300033d40f377acab7
parenta6851b8ec4044eef4af759a21887fdae6226e1cd (diff)
FIRRTL version support (#2543)
* Parse version and hardcode emitted version * Throw error if version is too high * Parse version even if rest is invalid * Change pattern match to if statement * Improve version grammar * Update tests * Remove outdated comment * Simplify grammar and use version class * Simplify and add no version test * Fix for conflicting lexer rule
-rw-r--r--src/main/antlr4/FIRRTL.g411
-rw-r--r--src/main/scala/firrtl/Parser.scala1
-rw-r--r--src/main/scala/firrtl/ir/Serializer.scala10
-rw-r--r--src/main/scala/firrtl/parser/Listener.scala11
-rw-r--r--src/test/scala/firrtlTests/ParserSpec.scala61
-rw-r--r--src/test/scala/firrtlTests/SerializerSpec.scala2
6 files changed, 94 insertions, 2 deletions
diff --git a/src/main/antlr4/FIRRTL.g4 b/src/main/antlr4/FIRRTL.g4
index d40c6560..10a87c9e 100644
--- a/src/main/antlr4/FIRRTL.g4
+++ b/src/main/antlr4/FIRRTL.g4
@@ -29,7 +29,16 @@ import firrtl.LexerHelper;
// Does there have to be at least one module?
circuit
- : 'circuit' id ':' info? INDENT module* DEDENT EOF
+ : version? NEWLINE* 'circuit' id ':' info? INDENT module* DEDENT EOF
+ ;
+
+version
+ : 'FIRRTL' 'version' semver NEWLINE
+ ;
+
+// Due to lexer problems, something like 1.1.0 is lexed as DoubleLit '.' UnsignedInt
+semver
+ : DoubleLit '.' UnsignedInt
;
module
diff --git a/src/main/scala/firrtl/Parser.scala b/src/main/scala/firrtl/Parser.scala
index bb93511c..b2d97e86 100644
--- a/src/main/scala/firrtl/Parser.scala
+++ b/src/main/scala/firrtl/Parser.scala
@@ -17,6 +17,7 @@ case class ParameterRedefinedException(message: String) extends ParserException(
case class InvalidStringLitException(message: String) extends ParserException(message)
case class InvalidEscapeCharException(message: String) extends ParserException(message)
case class SyntaxErrorsException(message: String) extends ParserException(message)
+case class UnsupportedVersionException(message: String) extends ParserException(message)
object Parser extends LazyLogging {
diff --git a/src/main/scala/firrtl/ir/Serializer.scala b/src/main/scala/firrtl/ir/Serializer.scala
index f5457dea..0666a4b1 100644
--- a/src/main/scala/firrtl/ir/Serializer.scala
+++ b/src/main/scala/firrtl/ir/Serializer.scala
@@ -6,10 +6,19 @@ import firrtl.Utils
import firrtl.backends.experimental.smt.random.DefRandom
import firrtl.constraint.Constraint
+case class Version(major: Int, minor: Int, patch: Int) {
+ def serialize: String = s"$major.$minor.$patch"
+ def incompatible(that: Version): Boolean =
+ this.major > that.major || (this.major == that.major && this.minor > that.minor)
+}
+
object Serializer {
val NewLine = '\n'
val Indent = " "
+ // The version supported by the serializer.
+ val version = Version(1, 1, 0)
+
/** Converts a `FirrtlNode` into its string representation with
* default indentation.
*/
@@ -254,6 +263,7 @@ object Serializer {
private def s(node: Circuit)(implicit b: StringBuilder, indent: Int): Unit = node match {
case Circuit(info, modules, main) =>
+ b ++= s"FIRRTL version ${version.serialize}\n"
b ++= "circuit "; b ++= main; b ++= " :"; s(info)
if (modules.nonEmpty) {
newLineNoIndent(); s(modules.head)(b, indent + 1)
diff --git a/src/main/scala/firrtl/parser/Listener.scala b/src/main/scala/firrtl/parser/Listener.scala
index ffaa22b2..1f6692ca 100644
--- a/src/main/scala/firrtl/parser/Listener.scala
+++ b/src/main/scala/firrtl/parser/Listener.scala
@@ -6,6 +6,7 @@ import firrtl.antlr.{FIRRTLParser, _}
import firrtl.Visitor
import firrtl.Parser.InfoMode
import firrtl.ir._
+import firrtl.UnsupportedVersionException
import scala.collection.mutable
import scala.concurrent.{Await, Future}
@@ -25,6 +26,16 @@ private[firrtl] class Listener(infoMode: InfoMode) extends FIRRTLBaseListener {
}
override def exitCircuit(ctx: FIRRTLParser.CircuitContext): Unit = {
+ if (ctx.version != null) {
+ val version = ctx.version.semver.getText
+ val parts = version.split("\\.")
+ val (major, minor, patch) = (parts(0).toInt, parts(1).toInt, parts(2).toInt)
+ if (Version(major, minor, patch).incompatible(Serializer.version)) {
+ throw new UnsupportedVersionException(
+ s"FIRRTL version ${version} is not supported (greater than ${Serializer.version.serialize})"
+ )
+ }
+ }
info = Some(visitor.visitInfo(Option(ctx.info), ctx))
main = Some(ctx.id.getText)
ctx.children = null // Null out to save memory
diff --git a/src/test/scala/firrtlTests/ParserSpec.scala b/src/test/scala/firrtlTests/ParserSpec.scala
index 89e73cb7..610025ea 100644
--- a/src/test/scala/firrtlTests/ParserSpec.scala
+++ b/src/test/scala/firrtlTests/ParserSpec.scala
@@ -92,6 +92,67 @@ class ParserSpec extends FirrtlFlatSpec {
) ++ PrimOps.listing
}
+ // ********** FIRRTL version number **********
+ "Version 1.1.0" should "be accepted" in {
+ val input = """
+ |FIRRTL version 1.1.0
+ |circuit Test :
+ | module Test :
+ | input in : UInt<1>
+ | in <= UInt(0)
+ """.stripMargin
+ val c = firrtl.Parser.parse(input)
+ firrtl.Parser.parse(c.serialize)
+ }
+
+ "Version 1.1.1" should "be accepted" in {
+ val input = """
+ |FIRRTL version 1.1.1
+ |circuit Test :
+ | module Test :
+ | input in : UInt<1>
+ | in <= UInt(0)
+ """.stripMargin
+ val c = firrtl.Parser.parse(input)
+ firrtl.Parser.parse(c.serialize)
+ }
+
+ "No version" should "be accepted" in {
+ val input = """
+ |circuit Test :
+ | module Test :
+ | input in : { 0 : { 0 : { 0 : UInt<32>, flip 1 : UInt<32> } } }
+ | in.0.0.1 <= in.0.0.0
+ """.stripMargin
+ val c = firrtl.Parser.parse(input)
+ firrtl.Parser.parse(c.serialize)
+ }
+
+ an[UnsupportedVersionException] should be thrownBy {
+ val input = """
+ |FIRRTL version 1.2.0
+ |circuit Test :
+ | module Test :
+ | input in : UInt<1>
+ | in <= UInt(0)
+ """.stripMargin
+ firrtl.Parser.parse(input)
+ }
+
+ an[UnsupportedVersionException] should be thrownBy {
+ val input = """
+ |FIRRTL version 2.0.0
+ |crcuit Test :
+ | module Test @@#!# :
+ | input in1 : UInt<2>
+ | input in2 : UInt<3>
+ | output out : UInt<4>
+ | out[1:0] <= in1
+ | out[3:2] <= in2[1:0]
+ """.stripMargin
+ firrtl.Parser.parse(input)
+ }
+
// ********** Memories **********
"Memories" should "allow arbitrary ordering of fields" in {
val fields = MemTests.fieldsToSeq(MemTests.fields)
diff --git a/src/test/scala/firrtlTests/SerializerSpec.scala b/src/test/scala/firrtlTests/SerializerSpec.scala
index aae0ff1f..797a6045 100644
--- a/src/test/scala/firrtlTests/SerializerSpec.scala
+++ b/src/test/scala/firrtlTests/SerializerSpec.scala
@@ -63,7 +63,7 @@ object SerializerSpec {
val childModuleTabbed: String = tab(childModule)
val simpleCircuit: String =
- "circuit test :\n" + childModuleTabbed + "\n\n" + testModuleTabbed + "\n"
+ s"FIRRTL version ${Serializer.version.serialize}\ncircuit test :\n" + childModuleTabbed + "\n\n" + testModuleTabbed + "\n"
}
class SerializerSpec extends AnyFlatSpec with Matchers {