diff options
| author | Schuyler Eldridge | 2021-06-16 20:40:47 -0400 |
|---|---|---|
| committer | GitHub | 2021-06-17 00:40:47 +0000 |
| commit | d708d3f0555bb940ff417d3574c6902bb7a9c853 (patch) | |
| tree | 0714c90b8ae4502dca0bdaf116d7a278b14faa8e /src | |
| parent | 117054bb4cdc3c5abf34ba5c99f61bcd590871f0 (diff) | |
Add Protocol Buffer emission (#2271)
* Add Protocol Buffer emission export
This adds infrastructure and annotations that let a user emit a FIRRTL
circuit as a Protocol Buffer.
Fixes #1696.
Signed-off-by: Schuyler Eldridge <schuyler.eldridge@sifive.com>
* fixup! Add Protocol Buffer emission export
Diffstat (limited to 'src')
| -rw-r--r-- | src/main/scala/firrtl/Emitter.scala | 44 | ||||
| -rw-r--r-- | src/main/scala/firrtl/backends/proto/ProtoBufEmitter.scala | 119 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/stage/FirrtlMainSpec.scala | 7 |
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( |
