aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSchuyler Eldridge2020-08-11 17:45:10 -0400
committerGitHub2020-08-11 21:45:10 +0000
commit8bdbbac28cbec95181dfd9742b2ac614f6833d01 (patch)
tree293a22435fc6ceed1200dba159cb68a14e7d22c4
parentf7cffd230ede9a483f183182fa8e1bea3c4cdd67 (diff)
File Serialization of Annotations (#1277)
* Transform, not run in LegalizeAndReduction test Switch from using FirrtlStage.transform to FirrtlStage.run in one test. The latter is problematic as it doesn't include wrappers or pre/post phases which are how things will work in the future for doing file writing (via HowToSerialize ideas). Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> * Use execute in FIRRTL testing infra (not run) Changes the FirrtlStage method in FIRRTL testing infrastructure from "run" (which does not include Stage-global Phases) to "execute" (which does). Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> * Add HowToSerialize Annotation mix-in This adds an Annotation mix-in, HowToSerialize, that allows an annotation to declare how it should be serialized to a file. The mix-in is abstract in a baseFileName and a suffix (used to generate a filename), a howToSerialize method (defining the string contents of the file), and a howToResume method (that defines a replacement for the file-serialized annotation that allows this to be resumed) [^1]. A default implementation for generating a filename (called filename) is defined that will put the baseFileName+suffix file in the target directory. This can be overridden by the annotation if desired. [^1]: When an annotation is serialized to a file, it should be removed from the emitted JSON-serialized annotations. The howToResume method defines a way of adding replacement annotations to the JSON-serialized annotations that tell a downstream tool how to find the serialized file. E.g., if a FIRRTL circuit is written to a file, this could be used to add a FirrtlFileAnnotation defining the location of the new file. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> * Handle HowToSerialize in WriteOutputAnnotations This extends firrtl.options.phase.WriteOutputAnnotations to serialize HowToSerialize annotations to files. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> * Test HowToSerialize in WriteOutputAnnotationsSpec This adds tests of the HowToSerialize mix-in inside the WriteOutputAnnotationsSpec. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> * [skip chisel tests] Migrate to HowToSerialize This migrates EmittedAnnotations (and its children) to mixin the HowToSerialize trait. This enables this annotations to be automatically written to files via WriteOutputAnnotations Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> * Deprecated firrtl.stage.phases.WriteEmitted Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> * Use streams in HowToSerialize This converts the HowToSerialize trait to use a Stream[Char] when defining how an annotation should be serialized. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> * Switch from Stream[Char] to Stream[Byte] Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> * Change howToSerialize method to Iterable Change the type of the HowToSerialize.howToSerialize method from a stream to an iterable. Using the latter (the superset of both lazy streams and non-lazy things like String) avoids problems with users having to choose laziness when they already have an eager object. In effect, this makes the API more general. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> * Add Scaladoc to HowToSerialize trait Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> * Change HowToSerialize to CustomFileEmission Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> * Add default implementation of replacements Add a default implementation of CustomFileEmission.replacements. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> * Avoid unnecessary 2x monad in CustomFileEmission Change the type of CustomFileEmission.replacements from Option[AnnotationSeq] to AnnotationSeq. The latter has all the properties of the former that I'm trying to express here: (1) can emptiness and (2) monadicity (if the AnnotationSeq is converted to a sequence first). The latter property is exploited in the WriteOutputAnnotations phase to concisely flatMap over the annotations and doing the double-monad is unnecessary. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> * Restrict CustomFileEmission filename API Change the API of CustomFileEmission to use a final def for the actual filename. The baseFileName is then made a method with an AnnotationSeq parameter to allow the filename to change as a function of other annotations, e.g., by an output circuit annotation. By restricting this API, we have more control over the default behavior of where things are written using the fixed behavior of the filename method---files will always be written using the behavior that StageOptions define. Previously, if users want customized behavior, they would need to duplicate this StageOptions functionality (and likely subtly deviate from the standard behavior and introduce problems with their build). Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> * Add file conflict behavior for CustomFileEmission Set behavior of file conflicts in CustomFileEmission to be the following: No two annotations in the same annotation sequence can serialize to the same file during the WriteOutputAnnotations phase. However, if the output annotation file already exists, it will be overwritten. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> * Return relative path from getBuildFileName Change FirrtlOptions.getBuildFileName to simply serialize the underlying Java File instead of converting this to its canonical path. This should improve the relocatability of files produced by the CustomFileEmission API. Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> * Normalize paths in StageOptions.getBuildFile Normalize paths inside the getBuildFileName utility of StageOptions. Add a check to prevent a null pointer dereference. Co-authored-by: Jack Koenig <koenig@sifive.com> Co-authored-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> * Refer to CustomFIleEmission in deprecation message Co-authored-by: Jack Koenig <koenig@sifive.com> Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> * Simplify CustomFileEmission toBytes implementation Co-authored-by: Jack Koenig <koenig@sifive.com> Co-authored-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> * Use toBytes, not getBytes, in CustomFileEmission Signed-off-by: Schuyler Eldridge <schuyler.eldridge@ibm.com> Co-authored-by: Jack Koenig <koenig@sifive.com>
-rw-r--r--src/main/scala/firrtl/Emitter.scala53
-rw-r--r--src/main/scala/firrtl/options/StageAnnotations.scala60
-rw-r--r--src/main/scala/firrtl/options/StageOptions.scala10
-rw-r--r--src/main/scala/firrtl/options/phases/WriteOutputAnnotations.scala41
-rw-r--r--src/main/scala/firrtl/stage/FirrtlStage.scala3
-rw-r--r--src/main/scala/firrtl/stage/package.scala4
-rw-r--r--src/main/scala/firrtl/stage/phases/Compiler.scala2
-rw-r--r--src/main/scala/firrtl/stage/phases/WriteEmitted.scala2
-rw-r--r--src/test/scala/firrtl/stage/phases/tests/DriverCompatibilitySpec.scala2
-rw-r--r--src/test/scala/firrtl/testutils/FirrtlSpec.scala6
-rw-r--r--src/test/scala/firrtlTests/execution/VerilogExecution.scala3
-rw-r--r--src/test/scala/firrtlTests/options/phases/WriteOutputAnnotationsSpec.scala61
-rw-r--r--src/test/scala/firrtlTests/transforms/LegalizeReductions.scala2
13 files changed, 208 insertions, 41 deletions
diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala
index b8d26103..432fa59e 100644
--- a/src/main/scala/firrtl/Emitter.scala
+++ b/src/main/scala/firrtl/Emitter.scala
@@ -14,11 +14,14 @@ import firrtl.PrimOps._
import firrtl.WrappedExpression._
import Utils._
import MemPortUtils.{memPortField, memType}
-import firrtl.options.{HasShellOptions, PhaseException, ShellOption, Unserializable}
-import firrtl.stage.{RunFirrtlTransformAnnotation, TransformManager}
+import firrtl.options.{HasShellOptions, CustomFileEmission, ShellOption, PhaseException}
+import firrtl.options.Viewer.view
+import firrtl.stage.{FirrtlFileAnnotation, FirrtlOptions, RunFirrtlTransformAnnotation, TransformManager}
// Datastructures
import scala.collection.mutable.ArrayBuffer
+import java.io.File
+
case class EmitterException(message: String) extends PassException(message)
// ***** Annotations for telling the Emitters what to emit *****
@@ -92,17 +95,35 @@ final case class EmittedFirrtlModule(name: String, value: String, outputSuffix:
final case class EmittedVerilogModule(name: String, value: String, outputSuffix: String) extends EmittedModule
/** Traits for Annotations containing emitted components */
-sealed trait EmittedAnnotation[T <: EmittedComponent] extends NoTargetAnnotation with Unserializable {
+sealed trait EmittedAnnotation[T <: EmittedComponent] extends NoTargetAnnotation with CustomFileEmission {
val value: T
+
+ override protected def baseFileName(annotations: AnnotationSeq): String = {
+ view[FirrtlOptions](annotations).outputFileName.getOrElse(value.name)
+ }
+
+ override protected val suffix: Option[String] = Some(value.outputSuffix)
+
+}
+sealed trait EmittedCircuitAnnotation[T <: EmittedCircuit] extends EmittedAnnotation[T] {
+
+ override def getBytes = value.value.getBytes
+
+}
+sealed trait EmittedModuleAnnotation[T <: EmittedModule] extends EmittedAnnotation[T] {
+
+ override def getBytes = value.value.getBytes
+
}
-sealed trait EmittedCircuitAnnotation[T <: EmittedCircuit] extends EmittedAnnotation[T]
-sealed trait EmittedModuleAnnotation[T <: EmittedModule] extends EmittedAnnotation[T]
case class EmittedFirrtlCircuitAnnotation(value: EmittedFirrtlCircuit)
- extends EmittedCircuitAnnotation[EmittedFirrtlCircuit]
+ extends EmittedCircuitAnnotation[EmittedFirrtlCircuit] {
+
+ override def replacements(file: File): AnnotationSeq = Seq(FirrtlFileAnnotation(file.toString))
+
+}
case class EmittedVerilogCircuitAnnotation(value: EmittedVerilogCircuit)
extends EmittedCircuitAnnotation[EmittedVerilogCircuit]
-
case class EmittedFirrtlModuleAnnotation(value: EmittedFirrtlModule)
extends EmittedModuleAnnotation[EmittedFirrtlModule]
case class EmittedVerilogModuleAnnotation(value: EmittedVerilogModule)
@@ -465,10 +486,10 @@ class VerilogEmitter extends SeqTransform with Emitter {
throw EmitterException("Cannot emit verification statements in Verilog" +
"(2001). Use the SystemVerilog emitter instead.")
}
-
- /**
+
+ /**
* Store Emission option per Target
- * Guarantee only one emission option per Target
+ * Guarantee only one emission option per Target
*/
private[firrtl] class EmissionOptionMap[V <: EmissionOption](val df : V) {
private val m = collection.mutable.HashMap[ReferenceTarget, V]().withDefaultValue(df)
@@ -480,9 +501,9 @@ class VerilogEmitter extends SeqTransform with Emitter {
}
def apply(key: ReferenceTarget): V = m.apply(key)
}
-
+
/** Provide API to retrieve EmissionOptions based on the provided [[AnnotationSeq]]
- *
+ *
* @param annotations : AnnotationSeq to be searched for EmissionOptions
*
*/
@@ -500,16 +521,16 @@ class VerilogEmitter extends SeqTransform with Emitter {
def getRegisterEmissionOption(target: ReferenceTarget): RegisterEmissionOption =
registerEmissionOption(target)
-
+
def getWireEmissionOption(target: ReferenceTarget): WireEmissionOption =
wireEmissionOption(target)
-
+
def getPortEmissionOption(target: ReferenceTarget): PortEmissionOption =
portEmissionOption(target)
-
+
def getNodeEmissionOption(target: ReferenceTarget): NodeEmissionOption =
nodeEmissionOption(target)
-
+
def getConnectEmissionOption(target: ReferenceTarget): ConnectEmissionOption =
connectEmissionOption(target)
diff --git a/src/main/scala/firrtl/options/StageAnnotations.scala b/src/main/scala/firrtl/options/StageAnnotations.scala
index 51d1c268..32f8ff59 100644
--- a/src/main/scala/firrtl/options/StageAnnotations.scala
+++ b/src/main/scala/firrtl/options/StageAnnotations.scala
@@ -4,6 +4,9 @@ package firrtl.options
import firrtl.AnnotationSeq
import firrtl.annotations.{Annotation, NoTargetAnnotation}
+import firrtl.options.Viewer.view
+
+import java.io.File
import scopt.OptionParser
@@ -14,6 +17,63 @@ sealed trait StageOption { this: Annotation => }
*/
trait Unserializable { this: Annotation => }
+/** Mix-in that lets an [[firrtl.annotations.Annotation Annotation]] serialize itself to a file separate from the output
+ * annotation file.
+ *
+ * This can be used as a mechanism to serialize an [[firrtl.options.Unserializable Unserializable]] annotation or to
+ * write ancillary collateral used by downstream tooling, e.g., a TCL script or an FPGA constraints file. Any
+ * annotations using this mix-in will be serialized by the [[firrtl.options.phases.WriteOutputAnnotations
+ * WriteOutputAnnotations]] phase. This is one of the last phases common to all [[firrtl.options.Stage Stages]] and
+ * should not have to be called/included manually.
+ *
+ * Note: from the perspective of transforms generating annotations that mix-in this trait, the serialized files are not
+ * expected to be available to downstream transforms. Communication of information between transforms must occur
+ * through the annotations that will eventually be serialized to files.
+ */
+trait CustomFileEmission { this: Annotation =>
+
+ /** Output filename where serialized content will be written
+ *
+ * The full annotation sequence is a parameter to allow for the location where this annotation will be serialized to
+ * be a function of other annotations, e.g., if the location where information is written is controlled by a separate
+ * file location annotation.
+ *
+ * @param annotations the annotation sequence at the time of emission
+ */
+ protected def baseFileName(annotations: AnnotationSeq): String
+
+ /** Optional suffix of the output file */
+ protected def suffix: Option[String]
+
+ /** A method that can convert this annotation to bytes that will be written to a file.
+ *
+ * If you only need to serialize a string, you can use the `getBytes` method:
+ * {{{
+ * def getBytes: Iterable[Byte] = myString.getBytes
+ * }}}
+ */
+ def getBytes: Iterable[Byte]
+
+ /** Optionally, a sequence of annotations that will replace this annotation in the output annotation file.
+ *
+ * A non-empty implementation of this method is a mechanism for telling a downstream [[firrtl.options.Stage Stage]]
+ * how to deserialize the information that was serialized to a separate file. For example, if a FIRRTL circuit is
+ * serialized to a separate file, this method could include an input file annotation that a later stage can use to
+ * read the serialized FIRRTL circuit back in.
+ */
+ def replacements(file: File): AnnotationSeq = Seq.empty
+
+ /** Method that returns the filename where this annotation will be serialized.
+ *
+ * @param annotations the annotations at the time of serialization
+ */
+ final def filename(annotations: AnnotationSeq): File = {
+ val name = view[StageOptions](annotations).getBuildFileName(baseFileName(annotations), suffix)
+ new File(name)
+ }
+
+}
+
/** Holds the name of the target directory
* - set with `-td/--target-dir`
* - if unset, a [[TargetDirAnnotation]] will be generated with the
diff --git a/src/main/scala/firrtl/options/StageOptions.scala b/src/main/scala/firrtl/options/StageOptions.scala
index 7905799c..f60a991c 100644
--- a/src/main/scala/firrtl/options/StageOptions.scala
+++ b/src/main/scala/firrtl/options/StageOptions.scala
@@ -59,11 +59,13 @@ class StageOptions private [firrtl] (
} else {
new File(targetDir + "/" + f)
}
- }.getCanonicalFile
+ }.toPath.normalize.toFile
- val parent = file.getParentFile
-
- if (!parent.exists) { parent.mkdirs() }
+ file.getParentFile match {
+ case null =>
+ case parent if (!parent.exists) => parent.mkdirs()
+ case _ =>
+ }
file.toString
}
diff --git a/src/main/scala/firrtl/options/phases/WriteOutputAnnotations.scala b/src/main/scala/firrtl/options/phases/WriteOutputAnnotations.scala
index 7d857108..ccd08fa4 100644
--- a/src/main/scala/firrtl/options/phases/WriteOutputAnnotations.scala
+++ b/src/main/scala/firrtl/options/phases/WriteOutputAnnotations.scala
@@ -3,11 +3,12 @@
package firrtl.options.phases
import firrtl.AnnotationSeq
-import firrtl.annotations.{DeletedAnnotation, JsonProtocol}
-import firrtl.options.{Phase, StageOptions, Unserializable, Viewer}
-import firrtl.options.Dependency
+import firrtl.annotations.{Annotation, DeletedAnnotation, JsonProtocol}
+import firrtl.options.{CustomFileEmission, Dependency, Phase, PhaseException, StageOptions, Unserializable, Viewer}
-import java.io.PrintWriter
+import java.io.{BufferedWriter, File, FileWriter, PrintWriter}
+
+import scala.collection.mutable
/** [[firrtl.options.Phase Phase]] that writes an [[AnnotationSeq]] to a file. A file is written if and only if a
* [[StageOptions]] view has a non-empty [[StageOptions.annotationFileOut annotationFileOut]].
@@ -27,10 +28,34 @@ class WriteOutputAnnotations extends Phase {
/** Write the input [[AnnotationSeq]] to a fie. */
def transform(annotations: AnnotationSeq): AnnotationSeq = {
val sopts = Viewer[StageOptions].view(annotations)
- val serializable = annotations.filter{
- case _: Unserializable => false
- case _: DeletedAnnotation => sopts.writeDeleted
- case _ => true
+ val filesWritten = mutable.HashMap.empty[String, Annotation]
+ val serializable: AnnotationSeq = annotations.toSeq.flatMap {
+ case _: Unserializable => None
+ case a: DeletedAnnotation => if (sopts.writeDeleted) { Some(a) } else { None }
+ case a: CustomFileEmission =>
+ val filename = a.filename(annotations)
+ val canonical = filename.getCanonicalPath()
+
+ filesWritten.get(canonical) match {
+ case None =>
+ val w = new BufferedWriter(new FileWriter(filename))
+ a.getBytes.foreach( w.write(_) )
+ w.close()
+ filesWritten(canonical) = a
+ case Some(first) =>
+ val msg =
+ s"""|Multiple CustomFileEmission annotations would be serialized to the same file, '$canonical'
+ | - first writer:
+ | class: ${first.getClass.getName}
+ | trimmed serialization: ${first.serialize.take(80)}
+ | - second writer:
+ | class: ${a.getClass.getName}
+ | trimmed serialization: ${a.serialize.take(80)}
+ |""".stripMargin
+ throw new PhaseException(msg)
+ }
+ a.replacements(filename)
+ case a => Some(a)
}
sopts.annotationFileOut match {
diff --git a/src/main/scala/firrtl/stage/FirrtlStage.scala b/src/main/scala/firrtl/stage/FirrtlStage.scala
index d26c5cff..1042f979 100644
--- a/src/main/scala/firrtl/stage/FirrtlStage.scala
+++ b/src/main/scala/firrtl/stage/FirrtlStage.scala
@@ -8,8 +8,7 @@ import firrtl.options.phases.DeletedWrapper
import firrtl.stage.phases.CatchExceptions
class FirrtlPhase
- extends PhaseManager(targets=Seq(Dependency[firrtl.stage.phases.Compiler],
- Dependency[firrtl.stage.phases.WriteEmitted])) {
+ extends PhaseManager(targets=Seq(Dependency[firrtl.stage.phases.Compiler])) {
override def invalidates(a: Phase) = false
diff --git a/src/main/scala/firrtl/stage/package.scala b/src/main/scala/firrtl/stage/package.scala
index 5bb7378d..e9cf3fb4 100644
--- a/src/main/scala/firrtl/stage/package.scala
+++ b/src/main/scala/firrtl/stage/package.scala
@@ -39,11 +39,9 @@ package object stage {
private [firrtl] implicit object FirrtlExecutionResultView extends OptionsView[FirrtlExecutionResult] with LazyLogging {
- private lazy val dummyWriteEmitted = new WriteEmitted
-
def view(options: AnnotationSeq): FirrtlExecutionResult = {
val emittedRes = options
- .collect{ case DeletedAnnotation(dummyWriteEmitted.name, a: EmittedAnnotation[_]) => a.value.value }
+ .collect{ case a: EmittedAnnotation[_] => a.value.value }
.mkString("\n")
val emitters = options.collect{ case RunFirrtlTransformAnnotation(e: Emitter) => e }
diff --git a/src/main/scala/firrtl/stage/phases/Compiler.scala b/src/main/scala/firrtl/stage/phases/Compiler.scala
index b3e902c8..b73e3058 100644
--- a/src/main/scala/firrtl/stage/phases/Compiler.scala
+++ b/src/main/scala/firrtl/stage/phases/Compiler.scala
@@ -51,7 +51,7 @@ class Compiler extends Phase with Translator[AnnotationSeq, Seq[CompilerRun]] {
Dependency[AddCircuit],
Dependency[AddImplicitOutputFile])
- override def optionalPrerequisiteOf = Seq(Dependency[WriteEmitted])
+ override def optionalPrerequisiteOf = Seq.empty
override def invalidates(a: Phase) = false
diff --git a/src/main/scala/firrtl/stage/phases/WriteEmitted.scala b/src/main/scala/firrtl/stage/phases/WriteEmitted.scala
index 98557aca..e2db2a94 100644
--- a/src/main/scala/firrtl/stage/phases/WriteEmitted.scala
+++ b/src/main/scala/firrtl/stage/phases/WriteEmitted.scala
@@ -24,6 +24,8 @@ import java.io.PrintWriter
*
* Any annotations written to files will be deleted.
*/
+@deprecated("Annotations that mixin the CustomFileEmission trait are automatically serialized by stages." +
+ "This will be removed in FIRRTL 1.5", "FIRRTL 1.4.0")
class WriteEmitted extends Phase {
override def prerequisites = Seq.empty
diff --git a/src/test/scala/firrtl/stage/phases/tests/DriverCompatibilitySpec.scala b/src/test/scala/firrtl/stage/phases/tests/DriverCompatibilitySpec.scala
index 64654175..007608ca 100644
--- a/src/test/scala/firrtl/stage/phases/tests/DriverCompatibilitySpec.scala
+++ b/src/test/scala/firrtl/stage/phases/tests/DriverCompatibilitySpec.scala
@@ -125,7 +125,7 @@ class DriverCompatibilitySpec extends AnyFlatSpec with Matchers with PrivateMeth
new PhaseFixture(new AddImplicitFirrtlFile) {
val annotations = Seq( TopNameAnnotation("foo") )
val expected = annotations.toSet +
- FirrtlFileAnnotation(new File("foo.fir").getCanonicalPath)
+ FirrtlFileAnnotation(new File("foo.fir").getPath())
phase.transform(annotations).toSet should be (expected)
}
diff --git a/src/test/scala/firrtl/testutils/FirrtlSpec.scala b/src/test/scala/firrtl/testutils/FirrtlSpec.scala
index 75739147..dfc20352 100644
--- a/src/test/scala/firrtl/testutils/FirrtlSpec.scala
+++ b/src/test/scala/firrtl/testutils/FirrtlSpec.scala
@@ -104,13 +104,13 @@ trait FirrtlRunners extends BackendCompilationUtilities {
val customName = s"${prefix}_custom"
val customAnnos = getBaseAnnos(customName) ++: toAnnos((new GetNamespace) +: customTransforms) ++: customAnnotations
- val customResult = (new firrtl.stage.FirrtlStage).run(customAnnos)
+ val customResult = (new firrtl.stage.FirrtlStage).execute(Array.empty, customAnnos)
val nsAnno = customResult.collectFirst { case m: ModuleNamespaceAnnotation => m }.get
val refSuggestedName = s"${prefix}_ref"
val refAnnos = getBaseAnnos(refSuggestedName) ++: Seq(RunFirrtlTransformAnnotation(new RenameModules), nsAnno)
- val refResult = (new firrtl.stage.FirrtlStage).run(refAnnos)
+ val refResult = (new firrtl.stage.FirrtlStage).execute(Array.empty, refAnnos)
val refName = refResult.collectFirst({ case stage.FirrtlCircuitAnnotation(c) => c.main }).getOrElse(refSuggestedName)
assert(BackendCompilationUtilities.yosysExpectSuccess(customName, refName, testDir, timesteps))
@@ -145,7 +145,7 @@ trait FirrtlRunners extends BackendCompilationUtilities {
annotations ++:
(customTransforms ++ extraCheckTransforms).map(RunFirrtlTransformAnnotation(_))
- (new firrtl.stage.FirrtlStage).run(annos)
+ (new firrtl.stage.FirrtlStage).execute(Array.empty, annos)
testDir
}
diff --git a/src/test/scala/firrtlTests/execution/VerilogExecution.scala b/src/test/scala/firrtlTests/execution/VerilogExecution.scala
index bf3d1461..89f27609 100644
--- a/src/test/scala/firrtlTests/execution/VerilogExecution.scala
+++ b/src/test/scala/firrtlTests/execution/VerilogExecution.scala
@@ -21,7 +21,8 @@ trait VerilogExecution extends TestExecution {
// Run FIRRTL, emit Verilog file
val cAnno = FirrtlCircuitAnnotation(c)
val tdAnno = TargetDirAnnotation(testDir.getAbsolutePath)
- (new FirrtlStage).run(AnnotationSeq(Seq(cAnno, tdAnno) ++ customAnnotations))
+
+ (new FirrtlStage).execute(Array.empty, AnnotationSeq(Seq(cAnno, tdAnno)) ++ customAnnotations)
// Copy harness resource to test directory
val harness = new File(testDir, s"top.cpp")
diff --git a/src/test/scala/firrtlTests/options/phases/WriteOutputAnnotationsSpec.scala b/src/test/scala/firrtlTests/options/phases/WriteOutputAnnotationsSpec.scala
index e71eaedf..0a3cce67 100644
--- a/src/test/scala/firrtlTests/options/phases/WriteOutputAnnotationsSpec.scala
+++ b/src/test/scala/firrtlTests/options/phases/WriteOutputAnnotationsSpec.scala
@@ -7,7 +7,16 @@ import java.io.File
import firrtl.AnnotationSeq
import firrtl.annotations.{DeletedAnnotation, NoTargetAnnotation}
-import firrtl.options.{InputAnnotationFileAnnotation, OutputAnnotationFileAnnotation, Phase, WriteDeletedAnnotation}
+import firrtl.options.{
+ CustomFileEmission,
+ InputAnnotationFileAnnotation,
+ OutputAnnotationFileAnnotation,
+ Phase,
+ PhaseException,
+ StageOptions,
+ TargetDirAnnotation,
+ WriteDeletedAnnotation}
+import firrtl.options.Viewer.view
import firrtl.options.phases.{GetIncludes, WriteOutputAnnotations}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
@@ -100,9 +109,59 @@ class WriteOutputAnnotationsSpec extends AnyFlatSpec with Matchers with firrtl.t
out.toSeq should be (annotations)
}
+ it should "write CustomFileEmission annotations" in new Fixture {
+ val file = new File("write-CustomFileEmission-annotations.anno.json")
+ val annotations = Seq( TargetDirAnnotation(dir),
+ OutputAnnotationFileAnnotation(file.toString),
+ WriteOutputAnnotationsSpec.Custom("hello!") )
+ val serializedFileName = view[StageOptions](annotations).getBuildFileName("Custom", Some(".Emission"))
+ val expected = annotations.map {
+ case _: WriteOutputAnnotationsSpec.Custom => WriteOutputAnnotationsSpec.Replacement(serializedFileName)
+ case a => a
+ }
+
+ val out = phase.transform(annotations)
+
+ info("annotations are unmodified")
+ out.toSeq should be (annotations)
+
+ fileContainsAnnotations(new File(dir, file.toString), expected)
+
+ info(s"file '$serializedFileName' exists")
+ new File(serializedFileName) should (exist)
+ }
+
+ it should "error if multiple annotations try to write to the same file" in new Fixture {
+ val file = new File("write-CustomFileEmission-annotations-error.anno.json")
+ val annotations = Seq( TargetDirAnnotation(dir),
+ OutputAnnotationFileAnnotation(file.toString),
+ WriteOutputAnnotationsSpec.Custom("foo"),
+ WriteOutputAnnotationsSpec.Custom("bar") )
+ intercept[PhaseException] {
+ phase.transform(annotations)
+ }.getMessage should startWith ("Multiple CustomFileEmission annotations")
+ }
+
}
private object WriteOutputAnnotationsSpec {
+
case object FooAnnotation extends NoTargetAnnotation
+
case class BarAnnotation(x: Int) extends NoTargetAnnotation
+
+ case class Custom(value: String) extends NoTargetAnnotation with CustomFileEmission {
+
+ override protected def baseFileName(a: AnnotationSeq): String = "Custom"
+
+ override protected def suffix: Option[String] = Some(".Emission")
+
+ override def getBytes: Iterable[Byte] = value.getBytes
+
+ override def replacements(file: File): AnnotationSeq = Seq(Replacement(file.toString))
+
+ }
+
+ case class Replacement(file: String) extends NoTargetAnnotation
+
}
diff --git a/src/test/scala/firrtlTests/transforms/LegalizeReductions.scala b/src/test/scala/firrtlTests/transforms/LegalizeReductions.scala
index 664701c3..5368c54c 100644
--- a/src/test/scala/firrtlTests/transforms/LegalizeReductions.scala
+++ b/src/test/scala/firrtlTests/transforms/LegalizeReductions.scala
@@ -65,7 +65,7 @@ circuit $name :
TargetDirAnnotation(testDir.toString) ::
CompilerAnnotation(new MinimumVerilogCompiler) ::
Nil
- val resultAnnos = (new FirrtlStage).run(annos)
+ val resultAnnos = (new FirrtlStage).transform(annos)
val outputFilename = resultAnnos.collectFirst { case OutputFileAnnotation(f) => f }
outputFilename.toRight(s"Output file not found!")
// Copy Verilator harness