diff options
| author | Zachary Yedidia | 2022-08-26 07:52:04 -0700 |
|---|---|---|
| committer | GitHub | 2022-08-26 14:52:04 +0000 |
| commit | 19fe90bb0fd37457c47f3873392db5cbb9b87d38 (patch) | |
| tree | 8d81c6b3d0ddf3b050a021300033d40f377acab7 | |
| parent | a6851b8ec4044eef4af759a21887fdae6226e1cd (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.g4 | 11 | ||||
| -rw-r--r-- | src/main/scala/firrtl/Parser.scala | 1 | ||||
| -rw-r--r-- | src/main/scala/firrtl/ir/Serializer.scala | 10 | ||||
| -rw-r--r-- | src/main/scala/firrtl/parser/Listener.scala | 11 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/ParserSpec.scala | 61 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/SerializerSpec.scala | 2 |
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 { |
