aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/scala/firrtl/Emitter.scala44
-rw-r--r--src/main/scala/firrtl/backends/proto/ProtoBufEmitter.scala119
-rw-r--r--src/test/scala/firrtlTests/stage/FirrtlMainSpec.scala7
3 files changed, 170 insertions, 0 deletions
diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala
index 7b77e324..7c91a544 100644
--- a/src/main/scala/firrtl/Emitter.scala
+++ b/src/main/scala/firrtl/Emitter.scala
@@ -6,6 +6,7 @@ import java.io.File
import firrtl.annotations.NoTargetAnnotation
import firrtl.backends.experimental.smt.{Btor2Emitter, SMTLibEmitter}
+import firrtl.backends.proto.{Emitter => ProtoEmitter}
import firrtl.options.Viewer.view
import firrtl.options.{CustomFileEmission, HasShellOptions, PhaseException, ShellOption}
import firrtl.passes.PassException
@@ -61,6 +62,49 @@ object EmitCircuitAnnotation extends HasShellOptions {
shortOption = Some("E"),
// the experimental options are intentionally excluded from the help message
helpValueName = Some("<chirrtl|high|middle|low|verilog|mverilog|sverilog>")
+ ),
+ new ShellOption[String](
+ longOption = "emit-circuit-protobuf",
+ toAnnotationSeq = (a: String) =>
+ a match {
+ case "chirrtl" =>
+ Seq(
+ RunFirrtlTransformAnnotation(new ProtoEmitter.Chirrtl),
+ EmitCircuitAnnotation(classOf[ProtoEmitter.Chirrtl])
+ )
+ case "mhigh" =>
+ Seq(
+ RunFirrtlTransformAnnotation(new ProtoEmitter.MHigh),
+ EmitCircuitAnnotation(classOf[ProtoEmitter.MHigh])
+ )
+ case "high" =>
+ Seq(
+ RunFirrtlTransformAnnotation(new ProtoEmitter.High),
+ EmitCircuitAnnotation(classOf[ProtoEmitter.High])
+ )
+ case "middle" =>
+ Seq(
+ RunFirrtlTransformAnnotation(new ProtoEmitter.Middle),
+ EmitCircuitAnnotation(classOf[ProtoEmitter.Middle])
+ )
+ case "low" =>
+ Seq(
+ RunFirrtlTransformAnnotation(new ProtoEmitter.Low),
+ EmitCircuitAnnotation(classOf[ProtoEmitter.Low])
+ )
+ case "low-opt" =>
+ Seq(
+ RunFirrtlTransformAnnotation(new ProtoEmitter.OptLow),
+ EmitCircuitAnnotation(classOf[ProtoEmitter.Low])
+ )
+ case _ => throw new PhaseException(s"Unknown emitter '$a'! (Did you misspell it?)")
+ },
+ helpText = "Run the specified circuit emitter generating a Protocol Buffer format",
+ shortOption = Some("P"),
+ // the experimental options are intentionally excluded from the help message
+ helpValueName = Some(
+ "<chirrtl|mhigh|high|middle|low|low-opt>"
+ )
)
)
}
diff --git a/src/main/scala/firrtl/backends/proto/ProtoBufEmitter.scala b/src/main/scala/firrtl/backends/proto/ProtoBufEmitter.scala
new file mode 100644
index 00000000..31edb6b8
--- /dev/null
+++ b/src/main/scala/firrtl/backends/proto/ProtoBufEmitter.scala
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: Apache-2.0
+package firrtl.backends.proto
+
+import firrtl.{AnnotationSeq, CircuitState, DependencyAPIMigration, Transform}
+import firrtl.ir
+import firrtl.annotations.NoTargetAnnotation
+import firrtl.options.CustomFileEmission
+import firrtl.options.Viewer.view
+import firrtl.proto.ToProto
+import firrtl.stage.{FirrtlOptions, Forms}
+import firrtl.stage.TransformManager.TransformDependency
+import java.io.{ByteArrayOutputStream, Writer}
+
+/** This object defines Annotations that are used by Protocol Buffer emission.
+ */
+object Annotation {
+
+ /** This will cause the enclosed circuit to be serialized to a Protocol Buffer with the provided suffix. */
+ case class ProtoBufSerialization(circuit: ir.Circuit, suffix: Option[String])
+ extends NoTargetAnnotation
+ with CustomFileEmission {
+
+ /** The hash code is cached for this case class because it includes a very large circuit operand whose hash is expensive
+ * to compute. This is safe to do because the case class is immutable.
+ */
+ override lazy val hashCode = scala.util.hashing.MurmurHash3.productHash(this)
+
+ override protected def baseFileName(annotations: AnnotationSeq): String = {
+ view[FirrtlOptions](annotations).outputFileName.getOrElse(circuit.main)
+ }
+
+ override def getBytes: Iterable[Byte] = {
+ val ostream = new java.io.ByteArrayOutputStream
+ ToProto.writeToStream(ostream, circuit)
+ ostream.toByteArray()
+ }
+ }
+
+}
+
+/** Running this transform will cause a circuit to be emitted to ProtoBuf after some prerequisites have been
+ * satisfied.
+ *
+ * This is not intended to be used directly. Instead see one of the concrete emitters, e.g., [[Emitter.Low]].
+ * @see [[Emitter.Chirrtl]]
+ * @see [[Emitter.MHigh]]
+ * @see [[Emitter.High]]
+ * @see [[Emitter.Middle]]
+ * @see [[Emitter.Low]]
+ * @see [[Emitter.OptLow]]
+ */
+sealed abstract class ProtoBufEmitter(prereqs: Seq[TransformDependency])
+ extends Transform
+ with DependencyAPIMigration
+ with firrtl.Emitter {
+
+ override def prerequisites = prereqs
+ override def optionalPrerequisites = Seq.empty
+ override def optionalPrerequisiteOf = Seq.empty
+ override def invalidates(a: Transform) = false
+
+ override def execute(state: CircuitState) =
+ state.copy(annotations = state.annotations :+ Annotation.ProtoBufSerialization(state.circuit, Some(outputSuffix)))
+
+ override def emit(state: CircuitState, writer: Writer): Unit = {
+ val ostream = new java.io.ByteArrayOutputStream
+ ToProto.writeToStream(ostream, state.circuit)
+ writer.write(ostream.toString())
+ }
+}
+
+/** This object defines different emitters that can be used to generate a Protocol Buffer of a FIRRTL circuit. */
+object Emitter {
+
+ /** Emit a FIRRTL circuit as ProtoBuf in CHIRRTL form. */
+ class Chirrtl extends ProtoBufEmitter(Forms.ChirrtlForm) {
+ override def outputSuffix = ".pb"
+ }
+
+ /** Emit a FIRRTL circuit as ProtoBuf in minimal High FIRRTL form.
+ *
+ * This will only have CHIRRTL constructs removed. The circuit will not be deduplicated nor have widths inferred.
+ * @see [[High]]
+ */
+ class MHigh extends ProtoBufEmitter(Forms.MinimalHighForm) {
+ override def outputSuffix = ".mhi.pb"
+ }
+
+ /** Emit a FIRRTL circuit as ProtoBuf in High FIRRTL form.
+ *
+ * The emitted circuit will be structurally deduplicated and have widths inferred.
+ * @see [[MHigh]]
+ */
+ class High extends ProtoBufEmitter(Forms.HighForm) {
+ override def outputSuffix = ".hi.pb"
+ }
+
+ /** Emit a FIRRTL circuit as ProtoBuf in Mid FIRRTL form. */
+ class Middle extends ProtoBufEmitter(Forms.MidForm) {
+ override def outputSuffix = ".mid.pb"
+ }
+
+ /** Emit a FIRRTL circuit as ProtoBuf in Low FIRRTL form without optimizations.
+ *
+ * @see [[OptLow]]
+ */
+ class Low extends ProtoBufEmitter(Forms.LowForm) {
+ override def outputSuffix = ".lo.pb"
+ }
+
+ /** Emit a FIRRTL circuit as ProtoBuf in Low FIRRTL form with optimizations.
+ *
+ * @see [[OptLow]]
+ */
+ class OptLow extends ProtoBufEmitter(Forms.LowFormOptimized) {
+ override def outputSuffix = ".lo.pb"
+ }
+
+}
diff --git a/src/test/scala/firrtlTests/stage/FirrtlMainSpec.scala b/src/test/scala/firrtlTests/stage/FirrtlMainSpec.scala
index b1f86a2b..d51fc164 100644
--- a/src/test/scala/firrtlTests/stage/FirrtlMainSpec.scala
+++ b/src/test/scala/firrtlTests/stage/FirrtlMainSpec.scala
@@ -213,6 +213,13 @@ class FirrtlMainSpec
args = Array("-X", "sverilog", "-E", "sverilog", "-foaf", "foo.json"),
files = Seq("Top.sv", "foo.json.anno.json")
),
+ /* Test all ProtoBuf emitters */
+ FirrtlMainTest(args = Array("-X", "none", "--emit-circuit-protobuf", "chirrtl"), files = Seq("Top.pb")),
+ FirrtlMainTest(args = Array("-X", "none", "-P", "mhigh"), files = Seq("Top.mhi.pb")),
+ FirrtlMainTest(args = Array("-X", "none", "-P", "high"), files = Seq("Top.hi.pb")),
+ FirrtlMainTest(args = Array("-X", "none", "-P", "middle"), files = Seq("Top.mid.pb")),
+ FirrtlMainTest(args = Array("-X", "none", "-P", "low"), files = Seq("Top.lo.pb")),
+ FirrtlMainTest(args = Array("-X", "none", "-P", "low-opt"), files = Seq("Top.lo.pb")),
/* Test all one file per module emitters */
FirrtlMainTest(args = Array("-X", "none", "-e", "chirrtl"), files = Seq("Top.fir", "Child.fir")),
FirrtlMainTest(