diff options
| author | Adam Izraelevitz | 2020-03-06 18:03:55 -0800 |
|---|---|---|
| committer | GitHub | 2020-03-07 02:03:55 +0000 |
| commit | dd72b24dde5b28aef4a3728fdb770e26f5dbc54d (patch) | |
| tree | 908ab02e878509a1734562851baa82732cfa8d18 | |
| parent | 140a29a851a9e5b0b1cd486cc5ba53c3ff763f27 (diff) | |
Add firrtl-json serializers (#1430)
* Add firrtl-json serializers
* Added support for ports, info. Added docs and tests
| -rw-r--r-- | src/main/scala/firrtl/Parser.scala | 122 | ||||
| -rw-r--r-- | src/main/scala/firrtl/annotations/JsonProtocol.scala | 36 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/annotationTests/JsonProtocolSpec.scala | 42 |
3 files changed, 199 insertions, 1 deletions
diff --git a/src/main/scala/firrtl/Parser.scala b/src/main/scala/firrtl/Parser.scala index ddc858e4..0388c453 100644 --- a/src/main/scala/firrtl/Parser.scala +++ b/src/main/scala/firrtl/Parser.scala @@ -63,8 +63,130 @@ object Parser extends LazyLogging { def parse(lines: Seq[String]): Circuit = parseString(lines.mkString("\n"), UseInfo) + + /** Parse the concrete syntax of a FIRRTL [[firrtl.ir.Circuit]], e.g. + * {{{ + * """circuit Top: + * | module Top: + * | input x: UInt + * | node y = x + * |""".stripMargin + * }}} + * becomes: + * {{{ + * Circuit( + * NoInfo, + * Seq(Module( + * NoInfo, + * "Top", + * Seq(Port(NoInfo, "x", Input, UIntType(UnknownWidth))), + * Block(DefNode(NoInfo, "y", Ref("x", UnknownType))) + * )), + * "Top" + * ) + * }}} + * @param text concrete Circuit syntax + * @return + */ def parse(text: String): Circuit = parseString(text, UseInfo) + /** Parse the concrete syntax of a FIRRTL [[firrtl.ir.Type]], e.g. + * "UInt<3>" becomes: + * {{{ + * UIntType(IntWidth(BigInt(3))) + * }}} + * @param tpe concrete Type syntax + * @return + */ + def parseType(tpe: String): Type = { + val input = Seq("circuit Top:\n", " module Top:\n", s" input x:$tpe\n") + val circuit = parse(input) + circuit.modules.head.ports.head.tpe + } + + /** Parse the concrete syntax of a FIRRTL [[firrtl.ir.Expression]], e.g. + * "add(x, y)" becomes: + * {{{ + * DoPrim(Add, Seq(Ref("x", UnknownType), Ref("y", UnknownType), Nil, UnknownType) + * }}} + * @param expr concrete Expression syntax + * @return + */ + def parseExpression(expr: String): Expression = { + val input = Seq("circuit Top:\n", " module Top:\n", s" node x = $expr\n") + val circuit = parse(input) + circuit.modules match { + case Seq(Module(_, _, _, Block(Seq(DefNode(_, _, value))))) => value + } + } + + /** Parse the concrete syntax of a FIRRTL [[firrtl.ir.Statement]], e.g. + * "wire x: UInt" becomes: + * {{{ + * DefWire(NoInfo, "x", UIntType(UnknownWidth)) + * }}} + * @param statement concrete Statement syntax + * @return + */ + def parseStatement(statement: String): Statement = { + val input = Seq("circuit Top:\n", " module Top:\n") ++ statement.split("\n").map(" " + _) + val circuit = parse(input) + circuit.modules.head.asInstanceOf[Module].body + } + + /** Parse the concrete syntax of a FIRRTL [[firrtl.ir.Port]], e.g. + * "input x: UInt" becomes: + * {{{ + * Port(NoInfo, "x", Input, UIntType(UnknownWidth)) + * }}} + * @param port concrete Port syntax + * @return + */ + def parsePort(port: String): Port = { + val input = Seq("circuit Top:\n", " module Top:\n", s" $port\n") + val circuit = parse(input) + circuit.modules.head.ports.head + } + + /** Parse the concrete syntax of a FIRRTL [[firrtl.ir.DefModule]], e.g. + * {{{ + * """module Top: + * | input x: UInt + * | node y = x + * |""".stripMargin + * }}} + * becomes: + * {{{ + * Module( + * NoInfo, + * "Top", + * Seq(Port(NoInfo, "x", Input, UIntType(UnknownWidth))), + * Block(DefNode(NoInfo, "y", Ref("x", UnknownType))) + * ) + * }}} + * @param module concrete DefModule syntax + * @return + */ + def parseDefModule(module: String): DefModule = { + val input = Seq("circuit Top:\n") ++ module.split("\n").map(" " + _) + val circuit = parse(input) + circuit.modules.head + } + + /** Parse the concrete syntax of a FIRRTL [[firrtl.ir.Info]], e.g. + * "@[FPU.scala 509:25]" becomes: + * {{{ + * FileInfo("FPU.scala 509:25") + * }}} + * @param info concrete Info syntax + * @return + */ + def parseInfo(info: String): Info = { + val input = Seq(s"circuit Top: $info\n", " module Top:\n", " input x: UInt\n") + val circuit = parse(input) + circuit.info + } + sealed abstract class InfoMode case object IgnoreInfo extends InfoMode diff --git a/src/main/scala/firrtl/annotations/JsonProtocol.scala b/src/main/scala/firrtl/annotations/JsonProtocol.scala index 55043b63..312b826a 100644 --- a/src/main/scala/firrtl/annotations/JsonProtocol.scala +++ b/src/main/scala/firrtl/annotations/JsonProtocol.scala @@ -1,7 +1,10 @@ +// See LICENSE for license details. package firrtl package annotations +import firrtl.ir._ + import scala.util.{Try, Failure} import org.json4s._ @@ -90,6 +93,35 @@ object JsonProtocol { { case JString(s) => Target.deserialize(s).asInstanceOf[CompleteTarget] }, { case named: CompleteTarget => JString(named.serialize) } )) + // FIRRTL Serializers + class TypeSerializer extends CustomSerializer[Type](format => ( + { case JString(s) => Parser.parseType(s) }, + { case tpe: Type => JString(tpe.serialize) } + )) + class ExpressionSerializer extends CustomSerializer[Expression](format => ( + { case JString(s) => Parser.parseExpression(s) }, + { case expr: Expression => JString(expr.serialize) } + )) + class StatementSerializer extends CustomSerializer[Statement](format => ( + { case JString(s) => Parser.parseStatement(s) }, + { case statement: Statement => JString(statement.serialize) } + )) + class PortSerializer extends CustomSerializer[Port](format => ( + { case JString(s) => Parser.parsePort(s) }, + { case port: Port => JString(port.serialize) } + )) + class DefModuleSerializer extends CustomSerializer[DefModule](format => ( + { case JString(s) => Parser.parseDefModule(s) }, + { case mod: DefModule => JString(mod.serialize) } + )) + class CircuitSerializer extends CustomSerializer[Circuit](format => ( + { case JString(s) => Parser.parse(s) }, + { case cir: Circuit => JString(cir.serialize) } + )) + class InfoSerializer extends CustomSerializer[Info](format => ( + { case JString(s) => Parser.parseInfo(s) }, + { case info: Info => JString(info.serialize) } + )) /** Construct Json formatter for annotations */ def jsonFormat(tags: Seq[Class[_]]) = { @@ -99,7 +131,9 @@ object JsonProtocol { new GenericTargetSerializer + new CircuitTargetSerializer + new ModuleTargetSerializer + new InstanceTargetSerializer + new ReferenceTargetSerializer + new TransformSerializer + new LoadMemoryFileTypeSerializer + new IsModuleSerializer + new IsMemberSerializer + - new CompleteTargetSerializer + new CompleteTargetSerializer + new TypeSerializer + new ExpressionSerializer + + new StatementSerializer + new PortSerializer + new DefModuleSerializer + + new CircuitSerializer + new InfoSerializer } /** Serialize annotations to a String for emission */ diff --git a/src/test/scala/firrtlTests/annotationTests/JsonProtocolSpec.scala b/src/test/scala/firrtlTests/annotationTests/JsonProtocolSpec.scala new file mode 100644 index 00000000..2dce89db --- /dev/null +++ b/src/test/scala/firrtlTests/annotationTests/JsonProtocolSpec.scala @@ -0,0 +1,42 @@ +// See LICENSE for license details. + +package firrtlTests.annotationTests + +import firrtl.Parser +import firrtl.annotations.{Annotation, JsonProtocol, NoTargetAnnotation} +import firrtl.ir._ +import org.scalatest.{FlatSpec, Matchers, PropSpec} + +case class AnAnnotation( + info: Info, + cir: Circuit, + mod: DefModule, + port: Port, + statement: Statement, + expr: Expression, + tpe: Type +) extends NoTargetAnnotation + +class JsonProtocolSpec extends FlatSpec with Matchers { + "JsonProtocol" should "serialize and deserialize FIRRTL types" in { + + val circuit = + """circuit Top: @[FPU.scala 509:25] + | module Top: + | input x: UInt + | output y: UInt + | y <= add(x, x) + |""".stripMargin + val cir = Parser.parse(circuit) + val mod = cir.modules.head + val port = mod.ports.head + val stmt = mod.asInstanceOf[Module].body + val expr = stmt.asInstanceOf[Block].stmts.head.asInstanceOf[Connect].expr + val tpe = port.tpe + val inputAnnos = Seq(AnAnnotation(cir.info, cir, mod, port, stmt, expr, tpe)) + val annosString = JsonProtocol.serialize(inputAnnos) + val outputAnnos = JsonProtocol.deserialize(annosString) + inputAnnos should be (outputAnnos) + } + +} |
