summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChick Markley2019-06-03 16:22:43 -0700
committerGitHub2019-06-03 16:22:43 -0700
commit821fe17b110e2a9017a335516227cb491c18cf43 (patch)
tree22fe46c8f675ca3739f893890efc2479f367d9a1
parent820e2688f0cb966d4528ff074fdbdc623ed8f940 (diff)
parent23641521174ba828feb32dc1c65c13a3d6b46970 (diff)
Merge pull request #1004 from freechipsproject/chisel-stage
Chisel stage
-rw-r--r--src/main/scala/chisel3/ChiselExecutionOptions.scala12
-rw-r--r--src/main/scala/chisel3/Driver.scala80
-rw-r--r--src/main/scala/chisel3/stage/ChiselAnnotations.scala105
-rw-r--r--src/main/scala/chisel3/stage/ChiselCli.scala12
-rw-r--r--src/main/scala/chisel3/stage/ChiselOptions.scala27
-rw-r--r--src/main/scala/chisel3/stage/ChiselStage.scala26
-rw-r--r--src/main/scala/chisel3/stage/package.scala58
-rw-r--r--src/main/scala/chisel3/stage/phases/AddImplicitOutputAnnotationFile.scala25
-rw-r--r--src/main/scala/chisel3/stage/phases/AddImplicitOutputFile.scala25
-rw-r--r--src/main/scala/chisel3/stage/phases/Checks.scala49
-rw-r--r--src/main/scala/chisel3/stage/phases/Convert.scala41
-rw-r--r--src/main/scala/chisel3/stage/phases/DriverCompatibility.scala115
-rw-r--r--src/main/scala/chisel3/stage/phases/Elaborate.scala42
-rw-r--r--src/main/scala/chisel3/stage/phases/Emitter.scala44
-rw-r--r--src/main/scala/chisel3/stage/phases/MaybeFirrtlStage.scala19
-rw-r--r--src/test/scala/chisel3/stage/phases/DriverCompatibilitySpec.scala70
-rw-r--r--src/test/scala/chiselTests/DriverSpec.scala9
-rw-r--r--src/test/scala/chiselTests/stage/ChiselAnnotationsSpec.scala65
-rw-r--r--src/test/scala/chiselTests/stage/ChiselOptionsViewSpec.scala40
-rw-r--r--src/test/scala/chiselTests/stage/phases/AddImplicitOutputAnnotationFileSpec.scala42
-rw-r--r--src/test/scala/chiselTests/stage/phases/AddImplicitOutputFileSpec.scala49
-rw-r--r--src/test/scala/chiselTests/stage/phases/ChecksSpec.scala43
-rw-r--r--src/test/scala/chiselTests/stage/phases/ConvertSpec.scala62
-rw-r--r--src/test/scala/chiselTests/stage/phases/ElaborateSpec.scala46
-rw-r--r--src/test/scala/chiselTests/stage/phases/EmitterSpec.scala60
25 files changed, 1110 insertions, 56 deletions
diff --git a/src/main/scala/chisel3/ChiselExecutionOptions.scala b/src/main/scala/chisel3/ChiselExecutionOptions.scala
index a3644829..eab49a3e 100644
--- a/src/main/scala/chisel3/ChiselExecutionOptions.scala
+++ b/src/main/scala/chisel3/ChiselExecutionOptions.scala
@@ -2,7 +2,9 @@
package chisel3
-import firrtl.{ExecutionOptionsManager, ComposableOptions}
+import chisel3.stage.{NoRunFirrtlCompilerAnnotation, PrintFullStackTraceAnnotation}
+
+import firrtl.{AnnotationSeq, ExecutionOptionsManager, ComposableOptions}
//TODO: provide support for running firrtl as separate process, could alternatively be controlled by external driver
//TODO: provide option for not saving chirrtl file, instead calling firrtl with in memory chirrtl
@@ -16,7 +18,13 @@ case class ChiselExecutionOptions(
runFirrtlCompiler: Boolean = true,
printFullStackTrace: Boolean = false
// var runFirrtlAsProcess: Boolean = false
- ) extends ComposableOptions
+ ) extends ComposableOptions {
+
+ def toAnnotations: AnnotationSeq =
+ (if (!runFirrtlCompiler) { Seq(NoRunFirrtlCompilerAnnotation) } else { Seq() }) ++
+ (if (printFullStackTrace) { Some(PrintFullStackTraceAnnotation) } else { None })
+
+}
trait HasChiselExecutionOptions {
self: ExecutionOptionsManager =>
diff --git a/src/main/scala/chisel3/Driver.scala b/src/main/scala/chisel3/Driver.scala
index 303f4599..906ae7fc 100644
--- a/src/main/scala/chisel3/Driver.scala
+++ b/src/main/scala/chisel3/Driver.scala
@@ -5,11 +5,15 @@ package chisel3
import chisel3.internal.ErrorLog
import chisel3.internal.firrtl._
import chisel3.experimental.{RawModule, RunFirrtlTransform}
+import chisel3.stage.{ChiselCircuitAnnotation, ChiselGeneratorAnnotation, ChiselStage, ChiselExecutionResultView}
+import chisel3.stage.phases.DriverCompatibility
import java.io._
import firrtl._
import firrtl.annotations.JsonProtocol
+import firrtl.options.Phase
+import firrtl.options.Viewer.view
import firrtl.util.{BackendCompilationUtilities => FirrtlBackendCompilationUtilities}
/**
@@ -196,8 +200,26 @@ object Driver extends BackendCompilationUtilities {
def execute( // scalastyle:ignore method.length
optionsManager: ExecutionOptionsManager with HasChiselExecutionOptions with HasFirrtlOptions,
dut: () => RawModule): ChiselExecutionResult = {
- val circuitOpt = try {
- Some(elaborate(dut))
+
+ val annos = ChiselGeneratorAnnotation(dut) +:
+ (optionsManager.chiselOptions.toAnnotations ++
+ optionsManager.firrtlOptions.toAnnotations ++
+ optionsManager.commonOptions.toAnnotations)
+
+ val phases: Seq[Phase] =
+ Seq( new DriverCompatibility.AddImplicitOutputFile,
+ new DriverCompatibility.AddImplicitOutputAnnotationFile,
+ new DriverCompatibility.DisableFirrtlStage,
+ new ChiselStage,
+ new DriverCompatibility.MutateOptionsManager(optionsManager),
+ new DriverCompatibility.ReEnableFirrtlStage,
+ new firrtl.stage.phases.DriverCompatibility.AddImplicitOutputFile,
+ new firrtl.stage.phases.DriverCompatibility.AddImplicitEmitter,
+ new chisel3.stage.phases.MaybeFirrtlStage )
+ .map(firrtl.options.phases.DeletedWrapper(_))
+
+ val annosx = try {
+ phases.foldLeft(annos)( (a, p) => p.transform(a) )
} catch {
case ce: ChiselException =>
val stackTrace = if (!optionsManager.chiselOptions.printFullStackTrace) {
@@ -208,60 +230,10 @@ object Driver extends BackendCompilationUtilities {
sw.toString
}
Predef.augmentString(stackTrace).lines.foreach(line => println(s"${ErrorLog.errTag} $line")) // scalastyle:ignore regex line.size.limit
- None
+ annos
}
- circuitOpt.map { circuit =>
- // this little hack let's us set the topName with the circuit name if it has not been set from args
- optionsManager.setTopNameIfNotSet(circuit.name)
-
- val firrtlOptions = optionsManager.firrtlOptions
- val chiselOptions = optionsManager.chiselOptions
-
- val firrtlCircuit = Converter.convert(circuit)
-
- // Still emit to leave an artifact (and because this always has been the behavior)
- val firrtlString = Driver.emit(circuit)
- val firrtlFileName = firrtlOptions.getInputFileName(optionsManager)
- val firrtlFile = new File(firrtlFileName)
-
- val w = new FileWriter(firrtlFile)
- w.write(firrtlString)
- w.close()
-
- // Emit the annotations because it has always been the behavior
- val annotationFile = new File(optionsManager.getBuildFileName("anno.json"))
- val af = new FileWriter(annotationFile)
- val firrtlAnnos = circuit.annotations.map(_.toFirrtl)
- af.write(JsonProtocol.serialize(firrtlAnnos))
- af.close()
-
- /** Find the set of transform classes associated with annotations then
- * instantiate an instance of each transform
- * @note Annotations targeting firrtl.Transform will not result in any
- * transform being instantiated
- */
- val transforms = circuit.annotations
- .collect { case anno: RunFirrtlTransform => anno.transformClass }
- .distinct
- .filterNot(_ == classOf[firrtl.Transform])
- .map { transformClass: Class[_ <: Transform] =>
- transformClass.newInstance()
- }
- /* This passes the firrtl source and annotations directly to firrtl */
- optionsManager.firrtlOptions = optionsManager.firrtlOptions.copy(
- firrtlCircuit = Some(firrtlCircuit),
- annotations = optionsManager.firrtlOptions.annotations ++ firrtlAnnos,
- customTransforms = optionsManager.firrtlOptions.customTransforms ++ transforms.toList)
-
- val firrtlExecutionResult = if(chiselOptions.runFirrtlCompiler) {
- Some(firrtl.Driver.execute(optionsManager))
- }
- else {
- None
- }
- ChiselExecutionSuccess(Some(circuit), firrtlString, firrtlExecutionResult)
- }.getOrElse(ChiselExecutionFailure("could not elaborate circuit"))
+ view[ChiselExecutionResult](annosx)
}
/**
diff --git a/src/main/scala/chisel3/stage/ChiselAnnotations.scala b/src/main/scala/chisel3/stage/ChiselAnnotations.scala
new file mode 100644
index 00000000..fb02173b
--- /dev/null
+++ b/src/main/scala/chisel3/stage/ChiselAnnotations.scala
@@ -0,0 +1,105 @@
+// See LICENSE for license details.
+
+package chisel3.stage
+
+import firrtl.annotations.{Annotation, NoTargetAnnotation}
+import firrtl.options.{HasShellOptions, OptionsException, ShellOption, Unserializable}
+
+import chisel3.{ChiselException, Module}
+import chisel3.experimental.RawModule
+import chisel3.internal.Builder
+import chisel3.internal.firrtl.Circuit
+
+/** Mixin that indicates that this is an [[firrtl.annotations.Annotation]] used to generate a [[ChiselOptions]] view.
+ */
+sealed trait ChiselOption extends Unserializable { this: Annotation => }
+
+/** Disable the execution of the FIRRTL compiler by Chisel
+ */
+case object NoRunFirrtlCompilerAnnotation extends NoTargetAnnotation with ChiselOption with HasShellOptions {
+
+ val options = Seq(
+ new ShellOption[Unit](
+ longOption = "no-run-firrtl",
+ toAnnotationSeq = _ => Seq(NoRunFirrtlCompilerAnnotation),
+ helpText = "Do not run the FIRRTL compiler (generate FIRRTL IR from Chisel and exit)",
+ shortOption = Some("chnrf") ) )
+
+}
+
+/** On an exception, this will cause the full stack trace to be printed as opposed to a pruned stack trace.
+ */
+case object PrintFullStackTraceAnnotation extends NoTargetAnnotation with ChiselOption with HasShellOptions {
+
+ val options = Seq(
+ new ShellOption[Unit](
+ longOption = "full-stacktrace",
+ toAnnotationSeq = _ => Seq(PrintFullStackTraceAnnotation),
+ helpText = "Show full stack trace when an exception is thrown" ) )
+
+}
+
+/** An [[firrtl.annotations.Annotation]] storing a function that returns a Chisel module
+ * @param gen a generator function
+ */
+case class ChiselGeneratorAnnotation(gen: () => RawModule) extends NoTargetAnnotation with Unserializable {
+
+ /** Run elaboration on the Chisel module generator function stored by this [[firrtl.annotations.Annotation]]
+ */
+ def elaborate: ChiselCircuitAnnotation = try {
+ ChiselCircuitAnnotation(Builder.build(Module(gen())))
+ } catch {
+ case e @ (_: OptionsException | _: ChiselException) => throw e
+ case e: Throwable =>
+ throw new OptionsException(s"Exception thrown when elaborating ChiselGeneratorAnnotation", e)
+ }
+
+}
+
+object ChiselGeneratorAnnotation extends HasShellOptions {
+
+ /** Construct a [[ChiselGeneratorAnnotation]] with a generator function that will try to construct a Chisel Module
+ * from using that Module's name. The Module must both exist in the class path and not take parameters.
+ * @param name a module name
+ * @throws firrtl.options.OptionsException if the module name is not found or if no parameterless constructor for
+ * that Module is found
+ */
+ def apply(name: String): ChiselGeneratorAnnotation = {
+ val gen = () => try {
+ Class.forName(name).asInstanceOf[Class[_ <: RawModule]].newInstance()
+ } catch {
+ case e: ClassNotFoundException =>
+ throw new OptionsException(s"Unable to locate module '$name'! (Did you misspell it?)", e)
+ case e: InstantiationException =>
+ throw new OptionsException(
+ s"Unable to create instance of module '$name'! (Does this class take parameters?)", e)
+ }
+ ChiselGeneratorAnnotation(gen)
+ }
+
+ val options = Seq(
+ new ShellOption[String](
+ longOption = "module",
+ toAnnotationSeq = (a: String) => Seq(ChiselGeneratorAnnotation(a)),
+ helpText = "The name of a Chisel module to elaborate (module must be in the classpath)",
+ helpValueName = Some("<package>.<module>") ) )
+
+}
+
+/** Stores a Chisel Circuit
+ * @param circuit a Chisel Circuit
+ */
+case class ChiselCircuitAnnotation(circuit: Circuit) extends NoTargetAnnotation with ChiselOption
+
+case class ChiselOutputFileAnnotation(file: String) extends NoTargetAnnotation with ChiselOption
+
+object ChiselOutputFileAnnotation extends HasShellOptions {
+
+ val options = Seq(
+ new ShellOption[String](
+ longOption = "chisel-output-file",
+ toAnnotationSeq = (a: String) => Seq(ChiselOutputFileAnnotation(a)),
+ helpText = "Write Chisel-generated FIRRTL to this file (default: <circuit-main>.fir)",
+ helpValueName = Some("<file>") ) )
+
+}
diff --git a/src/main/scala/chisel3/stage/ChiselCli.scala b/src/main/scala/chisel3/stage/ChiselCli.scala
new file mode 100644
index 00000000..4f7ac19e
--- /dev/null
+++ b/src/main/scala/chisel3/stage/ChiselCli.scala
@@ -0,0 +1,12 @@
+// See LICENSE for license details.
+
+package chisel3.stage
+
+import firrtl.options.Shell
+
+trait ChiselCli { this: Shell =>
+ parser.note("Chisel Front End Options")
+ Seq( NoRunFirrtlCompilerAnnotation,
+ PrintFullStackTraceAnnotation )
+ .foreach(_.addOptions(parser))
+}
diff --git a/src/main/scala/chisel3/stage/ChiselOptions.scala b/src/main/scala/chisel3/stage/ChiselOptions.scala
new file mode 100644
index 00000000..f7b9ccdf
--- /dev/null
+++ b/src/main/scala/chisel3/stage/ChiselOptions.scala
@@ -0,0 +1,27 @@
+// See LICENSE for license details.
+
+package chisel3.stage
+
+import chisel3.internal.firrtl.Circuit
+
+class ChiselOptions private[stage] (
+ val runFirrtlCompiler: Boolean = true,
+ val printFullStackTrace: Boolean = false,
+ val outputFile: Option[String] = None,
+ val chiselCircuit: Option[Circuit] = None) {
+
+ private[stage] def copy(
+ runFirrtlCompiler: Boolean = runFirrtlCompiler,
+ printFullStackTrace: Boolean = printFullStackTrace,
+ outputFile: Option[String] = outputFile,
+ chiselCircuit: Option[Circuit] = chiselCircuit ): ChiselOptions = {
+
+ new ChiselOptions(
+ runFirrtlCompiler = runFirrtlCompiler,
+ printFullStackTrace = printFullStackTrace,
+ outputFile = outputFile,
+ chiselCircuit = chiselCircuit )
+
+ }
+
+}
diff --git a/src/main/scala/chisel3/stage/ChiselStage.scala b/src/main/scala/chisel3/stage/ChiselStage.scala
new file mode 100644
index 00000000..1e92aaf6
--- /dev/null
+++ b/src/main/scala/chisel3/stage/ChiselStage.scala
@@ -0,0 +1,26 @@
+// See LICENSE for license details.
+
+package chisel3.stage
+
+import firrtl.AnnotationSeq
+import firrtl.options.{Phase, Shell, Stage}
+import firrtl.stage.FirrtlCli
+
+class ChiselStage extends Stage {
+ val shell: Shell = new Shell("chisel") with ChiselCli with FirrtlCli
+
+ private val phases: Seq[Phase] =
+ Seq( new chisel3.stage.phases.Checks,
+ new chisel3.stage.phases.Elaborate,
+ new chisel3.stage.phases.AddImplicitOutputFile,
+ new chisel3.stage.phases.AddImplicitOutputAnnotationFile,
+ new chisel3.stage.phases.Emitter,
+ new chisel3.stage.phases.Convert,
+ new chisel3.stage.phases.MaybeFirrtlStage )
+ .map(firrtl.options.phases.DeletedWrapper(_))
+
+ def run(annotations: AnnotationSeq): AnnotationSeq =
+ /* @todo: Should this be wrapped in a try/catch? */
+ phases.foldLeft(annotations)( (a, f) => f.transform(a) )
+
+}
diff --git a/src/main/scala/chisel3/stage/package.scala b/src/main/scala/chisel3/stage/package.scala
new file mode 100644
index 00000000..67d38ae7
--- /dev/null
+++ b/src/main/scala/chisel3/stage/package.scala
@@ -0,0 +1,58 @@
+// See LICENSE for license details.
+
+package chisel3
+
+import firrtl._
+import firrtl.annotations.DeletedAnnotation
+import firrtl.options.OptionsView
+
+import chisel3.internal.firrtl.{Circuit => ChiselCircuit}
+import chisel3.stage.phases.{Convert, Emitter}
+
+package object stage {
+
+ implicit object ChiselOptionsView extends OptionsView[ChiselOptions] {
+
+ def view(options: AnnotationSeq): ChiselOptions = options
+ .collect { case a: ChiselOption => a }
+ .foldLeft(new ChiselOptions()){ (c, x) =>
+ x match {
+ case _: NoRunFirrtlCompilerAnnotation.type => c.copy(runFirrtlCompiler = false)
+ case _: PrintFullStackTraceAnnotation.type => c.copy(printFullStackTrace = true)
+ case ChiselOutputFileAnnotation(f) => c.copy(outputFile = Some(f))
+ case ChiselCircuitAnnotation(a) => c.copy(chiselCircuit = Some(a))
+ }
+ }
+
+ }
+
+ private[chisel3] implicit object ChiselExecutionResultView extends OptionsView[ChiselExecutionResult] {
+
+ lazy val dummyWriteEmitted = new firrtl.stage.phases.WriteEmitted
+ lazy val dummyConvert = new Convert
+ lazy val dummyEmitter = new Emitter
+
+ def view(options: AnnotationSeq): ChiselExecutionResult = {
+ var chiselCircuit: Option[ChiselCircuit] = None
+ var chirrtlCircuit: Option[String] = None
+
+ options.foreach {
+ case DeletedAnnotation(dummyConvert.name, ChiselCircuitAnnotation(a)) => chiselCircuit = Some(a)
+ case DeletedAnnotation(dummyEmitter.name, EmittedFirrtlCircuitAnnotation(EmittedFirrtlCircuit(_, a, _))) =>
+ chirrtlCircuit = Some(a)
+ case _ =>
+ }
+
+ val fResult = firrtl.stage.phases.DriverCompatibility.firrtlResultView(options)
+
+ (chiselCircuit, chirrtlCircuit) match {
+ case (None, _) => ChiselExecutionFailure("Failed to elaborate Chisel circuit")
+ case (Some(_), None) => ChiselExecutionFailure("Failed to convert Chisel circuit to FIRRTL")
+ case (Some(a), Some(b)) => ChiselExecutionSuccess( Some(a), b, Some(fResult))
+ }
+
+ }
+
+ }
+
+}
diff --git a/src/main/scala/chisel3/stage/phases/AddImplicitOutputAnnotationFile.scala b/src/main/scala/chisel3/stage/phases/AddImplicitOutputAnnotationFile.scala
new file mode 100644
index 00000000..de251ab6
--- /dev/null
+++ b/src/main/scala/chisel3/stage/phases/AddImplicitOutputAnnotationFile.scala
@@ -0,0 +1,25 @@
+// See LICENSE for license details.
+
+package chisel3.stage.phases
+
+import chisel3.stage.ChiselCircuitAnnotation
+import firrtl.AnnotationSeq
+import firrtl.options.{OutputAnnotationFileAnnotation, Phase}
+
+/** Adds an [[firrtl.options.OutputAnnotationFileAnnotation]] if one does not exist. This replicates old behavior where
+ * an output annotation file was always written.
+ */
+class AddImplicitOutputAnnotationFile extends Phase {
+
+ def transform(annotations: AnnotationSeq): AnnotationSeq = annotations
+ .collectFirst{ case _: OutputAnnotationFileAnnotation => annotations }
+ .getOrElse{
+
+ val x: Option[AnnotationSeq] = annotations
+ .collectFirst{ case a: ChiselCircuitAnnotation =>
+ OutputAnnotationFileAnnotation(a.circuit.name) +: annotations }
+
+ x.getOrElse(annotations)
+ }
+
+}
diff --git a/src/main/scala/chisel3/stage/phases/AddImplicitOutputFile.scala b/src/main/scala/chisel3/stage/phases/AddImplicitOutputFile.scala
new file mode 100644
index 00000000..4a4dac72
--- /dev/null
+++ b/src/main/scala/chisel3/stage/phases/AddImplicitOutputFile.scala
@@ -0,0 +1,25 @@
+// See LICENSE for license details.
+
+package chisel3.stage.phases
+
+import firrtl.AnnotationSeq
+import firrtl.options.Phase
+
+import chisel3.stage.{ChiselCircuitAnnotation, ChiselOutputFileAnnotation}
+
+/** Add a output file for a Chisel circuit, derived from the top module in the circuit, if no
+ * [[ChiselOutputFileAnnotation]] already exists.
+ */
+class AddImplicitOutputFile extends Phase {
+
+ def transform(annotations: AnnotationSeq): AnnotationSeq =
+ annotations.collectFirst{ case _: ChiselOutputFileAnnotation => annotations }.getOrElse{
+
+ val x: Option[AnnotationSeq] = annotations
+ .collectFirst{ case a: ChiselCircuitAnnotation =>
+ ChiselOutputFileAnnotation(a.circuit.name) +: annotations }
+
+ x.getOrElse(annotations)
+ }
+
+}
diff --git a/src/main/scala/chisel3/stage/phases/Checks.scala b/src/main/scala/chisel3/stage/phases/Checks.scala
new file mode 100644
index 00000000..e2606019
--- /dev/null
+++ b/src/main/scala/chisel3/stage/phases/Checks.scala
@@ -0,0 +1,49 @@
+// See LICENSE for license details.
+
+package chisel3.stage.phases
+
+import chisel3.stage.{ChiselOutputFileAnnotation, NoRunFirrtlCompilerAnnotation, PrintFullStackTraceAnnotation}
+
+import firrtl.AnnotationSeq
+import firrtl.annotations.Annotation
+import firrtl.options.{OptionsException, Phase}
+
+/** Sanity checks an [[firrtl.AnnotationSeq]] before running the main [[firrtl.options.Phase]]s of
+ * [[chisel3.stage.ChiselStage]].
+ */
+class Checks extends Phase {
+
+ def transform(annotations: AnnotationSeq): AnnotationSeq = {
+ val noF, st, outF = collection.mutable.ListBuffer[Annotation]()
+ annotations.foreach {
+ case a: NoRunFirrtlCompilerAnnotation.type => a +=: noF
+ case a: PrintFullStackTraceAnnotation.type => a +=: st
+ case a: ChiselOutputFileAnnotation => a +=: outF
+ case _ =>
+ }
+
+ if (noF.size > 1) {
+ throw new OptionsException(
+ s"""|At most one NoRunFirrtlCompilerAnnotation can be specified, but found '${noF.size}'. Did you duplicate:
+ | - option or annotation: -chnrf, --no-run-firrtl, NoRunFirrtlCompilerAnnotation
+ |""".stripMargin)
+ }
+
+ if (st.size > 1) {
+ throw new OptionsException(
+ s"""|At most one PrintFullStackTraceAnnotation can be specified, but found '${noF.size}'. Did you duplicate:
+ | - option or annotation: --full-stacktrace, PrintFullStackTraceAnnotation
+ |""".stripMargin)
+ }
+
+ if (outF.size > 1) {
+ throw new OptionsException(
+ s"""|At most one Chisel output file can be specified but found '${outF.size}'. Did you duplicate:
+ | - option or annotation: --chisel-output-file, ChiselOutputFileAnnotation
+ |""".stripMargin)
+ }
+
+ annotations
+ }
+
+}
diff --git a/src/main/scala/chisel3/stage/phases/Convert.scala b/src/main/scala/chisel3/stage/phases/Convert.scala
new file mode 100644
index 00000000..174030ae
--- /dev/null
+++ b/src/main/scala/chisel3/stage/phases/Convert.scala
@@ -0,0 +1,41 @@
+// See LICENSE for license details.
+
+package chisel3.stage.phases
+
+import chisel3.experimental.RunFirrtlTransform
+import chisel3.internal.firrtl.Converter
+import chisel3.stage.ChiselCircuitAnnotation
+
+import firrtl.{AnnotationSeq, Transform}
+import firrtl.options.Phase
+import firrtl.stage.{FirrtlCircuitAnnotation, RunFirrtlTransformAnnotation}
+
+/** This prepares a [[ChiselCircuitAnnotation]] for compilation with FIRRTL. This does three things:
+ * - Uses [[chisel3.internal.firrtl.Converter]] to generate a [[FirrtlCircuitAnnotation]]
+ * - Extracts all [[firrtl.annotations.Annotation]]s from the [[chisel3.internal.firrtl.Circuit]]
+ * - Generates any needed [[RunFirrtlTransformAnnotation]]s from extracted [[firrtl.annotations.Annotation]]s
+ */
+class Convert extends Phase {
+
+ def transform(annotations: AnnotationSeq): AnnotationSeq = annotations.flatMap {
+ case a: ChiselCircuitAnnotation => {
+ /* Convert this Chisel Circuit to a FIRRTL Circuit */
+ Some(FirrtlCircuitAnnotation(Converter.convert(a.circuit))) ++
+ /* Convert all Chisel Annotations to FIRRTL Annotations */
+ a
+ .circuit
+ .annotations
+ .map(_.toFirrtl) ++
+ /* Add requested FIRRTL Transforms for any Chisel Annotations which mixed in RunFirrtlTransform */
+ a
+ .circuit
+ .annotations
+ .collect { case b: RunFirrtlTransform => b.transformClass }
+ .distinct
+ .filterNot(_ == classOf[firrtl.Transform])
+ .map { c: Class[_ <: Transform] => RunFirrtlTransformAnnotation(c.newInstance()) }
+ }
+ case a => Some(a)
+ }
+
+}
diff --git a/src/main/scala/chisel3/stage/phases/DriverCompatibility.scala b/src/main/scala/chisel3/stage/phases/DriverCompatibility.scala
new file mode 100644
index 00000000..b7674aa1
--- /dev/null
+++ b/src/main/scala/chisel3/stage/phases/DriverCompatibility.scala
@@ -0,0 +1,115 @@
+// See LICENSE for license details.
+
+package chisel3.stage.phases
+
+import firrtl.{AnnotationSeq, ExecutionOptionsManager, HasFirrtlOptions}
+import firrtl.annotations.NoTargetAnnotation
+import firrtl.options.{OutputAnnotationFileAnnotation, Phase}
+import firrtl.stage.{FirrtlCircuitAnnotation, RunFirrtlTransformAnnotation}
+import firrtl.stage.phases.DriverCompatibility.TopNameAnnotation
+
+import chisel3.HasChiselExecutionOptions
+import chisel3.stage.{NoRunFirrtlCompilerAnnotation, ChiselOutputFileAnnotation}
+
+/** This provides components of a compatibility wrapper around Chisel's deprecated [[chisel3.Driver]].
+ *
+ * Primarily, this object includes [[firrtl.options.Phase Phase]]s that generate [[firrtl.annotations.Annotation]]s
+ * derived from the deprecated [[firrtl.stage.phases.DriverCompatibility.TopNameAnnotation]].
+ */
+object DriverCompatibility {
+
+ /** Adds a [[ChiselOutputFileAnnotation]] derived from a [[TopNameAnnotation]] if no [[ChiselOutputFileAnnotation]]
+ * already exists. If no [[TopNameAnnotation]] exists, then no [[firrtl.stage.OutputFileAnnotation]] is added. ''This is not a
+ * replacement for [[chisel3.stage.phases.AddImplicitOutputFile AddImplicitOutputFile]] as this only adds an output
+ * file based on a discovered top name and not on a discovered elaborated circuit.'' Consequently, this will provide
+ * the correct behavior before a circuit has been elaborated.
+ * @note the output suffix is unspecified and will be set by the underlying [[firrtl.EmittedComponent]]
+ */
+ private[chisel3] class AddImplicitOutputFile extends Phase {
+
+ def transform(annotations: AnnotationSeq): AnnotationSeq = {
+ val hasOutputFile = annotations
+ .collectFirst{ case a: ChiselOutputFileAnnotation => a }
+ .isDefined
+ lazy val top = annotations.collectFirst{ case TopNameAnnotation(a) => a }
+
+ if (!hasOutputFile && top.isDefined) {
+ ChiselOutputFileAnnotation(top.get) +: annotations
+ } else {
+ annotations
+ }
+ }
+ }
+
+ /** If a [[firrtl.options.OutputAnnotationFileAnnotation]] does not exist, this adds one derived from a
+ * [[TopNameAnnotation]]. ''This is not a replacement for [[chisel3.stage.phases.AddImplicitOutputAnnotationFile]] as
+ * this only adds an output annotation file based on a discovered top name.'' Consequently, this will provide the
+ * correct behavior before a circuit has been elaborated.
+ * @note the output suffix is unspecified and will be set by [[firrtl.options.phases.WriteOutputAnnotations]]
+ */
+ private[chisel3] class AddImplicitOutputAnnotationFile extends Phase {
+
+ def transform(annotations: AnnotationSeq): AnnotationSeq =
+ annotations
+ .collectFirst{ case _: OutputAnnotationFileAnnotation => annotations }
+ .getOrElse{
+ val top = annotations.collectFirst{ case TopNameAnnotation(a) => a }
+ if (top.isDefined) {
+ OutputAnnotationFileAnnotation(top.get) +: annotations
+ } else {
+ annotations
+ }
+ }
+ }
+
+ private[chisel3] case object RunFirrtlCompilerAnnotation extends NoTargetAnnotation
+
+ /** Disables the execution of [[firrtl.stage.FirrtlStage]]. This can be used to call [[chisel3.stage.ChiselStage]] and
+ * guarantee that the FIRRTL compiler will not run. This is necessary for certain [[chisel3.Driver]] compatibility
+ * situations where you need to do something between Chisel compilation and FIRRTL compilations, e.g., update a
+ * mutable data structure.
+ */
+ private[chisel3] class DisableFirrtlStage extends Phase {
+
+ def transform(annotations: AnnotationSeq): AnnotationSeq = annotations
+ .collectFirst { case NoRunFirrtlCompilerAnnotation => annotations }
+ .getOrElse { Seq(RunFirrtlCompilerAnnotation, NoRunFirrtlCompilerAnnotation) ++ annotations }
+ }
+
+ private[chisel3] class ReEnableFirrtlStage extends Phase {
+
+ def transform(annotations: AnnotationSeq): AnnotationSeq = annotations
+ .collectFirst { case RunFirrtlCompilerAnnotation =>
+ val a: AnnotationSeq = annotations.filter {
+ case NoRunFirrtlCompilerAnnotation | RunFirrtlCompilerAnnotation => false
+ case _ => true
+ }
+ a
+ }
+ .getOrElse{ annotations }
+
+ }
+
+ /** Mutate an input [[firrtl.ExecutionOptionsManager]] based on information encoded in an [[firrtl.AnnotationSeq]].
+ * This is intended to be run between [[chisel3.stage.ChiselStage ChiselStage]] and [[firrtl.stage.FirrtlStage]] if
+ * you want to have backwards compatibility with an [[firrtl.ExecutionOptionsManager]].
+ */
+ private[chisel3] class MutateOptionsManager(
+ optionsManager: ExecutionOptionsManager with HasChiselExecutionOptions with HasFirrtlOptions) extends Phase {
+
+ def transform(annotations: AnnotationSeq): AnnotationSeq = {
+
+ val firrtlCircuit = annotations.collectFirst{ case FirrtlCircuitAnnotation(a) => a }
+ optionsManager.firrtlOptions = optionsManager.firrtlOptions.copy(
+ firrtlCircuit = firrtlCircuit,
+ annotations = optionsManager.firrtlOptions.annotations ++ annotations,
+ customTransforms = optionsManager.firrtlOptions.customTransforms ++
+ annotations.collect{ case RunFirrtlTransformAnnotation(a) => a } )
+
+ annotations
+
+ }
+
+ }
+
+}
diff --git a/src/main/scala/chisel3/stage/phases/Elaborate.scala b/src/main/scala/chisel3/stage/phases/Elaborate.scala
new file mode 100644
index 00000000..0b0d71fb
--- /dev/null
+++ b/src/main/scala/chisel3/stage/phases/Elaborate.scala
@@ -0,0 +1,42 @@
+// See LICENSE for license details.
+
+package chisel3.stage.phases
+
+import java.io.{PrintWriter, StringWriter}
+
+import chisel3.ChiselException
+import chisel3.internal.ErrorLog
+import chisel3.stage.{ChiselGeneratorAnnotation, ChiselOptions}
+import firrtl.AnnotationSeq
+import firrtl.options.Viewer.view
+import firrtl.options.{OptionsException, Phase}
+
+/** Elaborate all [[chisel3.stage.ChiselGeneratorAnnotation]]s into [[chisel3.stage.ChiselCircuitAnnotation]]s.
+ */
+class Elaborate extends Phase {
+
+ /**
+ * @todo Change this to print to STDERR (`Console.err.println`)
+ */
+ def transform(annotations: AnnotationSeq): AnnotationSeq = annotations.flatMap {
+ case a: ChiselGeneratorAnnotation =>
+ try {
+ Some(a.elaborate)
+ } catch {
+ case e: OptionsException => throw e
+ case e: ChiselException =>
+ val copts = view[ChiselOptions](annotations)
+ val stackTrace = if (!copts.printFullStackTrace) {
+ e.chiselStackTrace
+ } else {
+ val s = new StringWriter
+ e.printStackTrace(new PrintWriter(s))
+ s.toString
+ }
+ Predef.augmentString(stackTrace).lines.foreach(line => println(s"${ErrorLog.errTag} $line"))
+ Some(a)
+ }
+ case a => Some(a)
+ }
+
+}
diff --git a/src/main/scala/chisel3/stage/phases/Emitter.scala b/src/main/scala/chisel3/stage/phases/Emitter.scala
new file mode 100644
index 00000000..1bdb9f8d
--- /dev/null
+++ b/src/main/scala/chisel3/stage/phases/Emitter.scala
@@ -0,0 +1,44 @@
+// See LICENSE for license details.
+
+package chisel3.stage.phases
+
+import firrtl.{AnnotationSeq, EmittedFirrtlCircuit, EmittedFirrtlCircuitAnnotation}
+import firrtl.annotations.DeletedAnnotation
+import firrtl.options.{Phase, StageOptions}
+import firrtl.options.Viewer.view
+
+import chisel3.internal.firrtl.{Emitter => OldEmitter}
+import chisel3.stage.{ChiselCircuitAnnotation, ChiselOptions}
+
+import java.io.{File, FileWriter}
+
+/** Emit a [[chisel3.stage.ChiselCircuitAnnotation]] to a file if a [[chisel3.stage.ChiselOutputFileAnnotation]] is
+ * present. A deleted [[firrtl.EmittedFirrtlCircuitAnnotation]] is added.
+ *
+ * @todo This should be switched to support correct emission of multiple circuits to multiple files. The API should
+ * likely mirror how the [[firrtl.stage.phases.Compiler]] parses annotations into "global" annotations and
+ * left-associative per-circuit annotations.
+ * @todo The use of the deleted [[firrtl.EmittedFirrtlCircuitAnnotation]] is a kludge to provide some breadcrumbs such
+ * that the emitted CHIRRTL can be provided back to the old Driver. This should be removed or a better solution
+ * developed.
+ */
+class Emitter extends Phase {
+
+ def transform(annotations: AnnotationSeq): AnnotationSeq = {
+ val copts = view[ChiselOptions](annotations)
+ val sopts = view[StageOptions](annotations)
+
+ annotations.flatMap {
+ case a: ChiselCircuitAnnotation if copts.outputFile.isDefined =>
+ val file = new File(sopts.getBuildFileName(copts.outputFile.get, Some(".fir")))
+ val emitted = OldEmitter.emit(a.circuit)
+ val w = new FileWriter(file)
+ w.write(emitted)
+ w.close()
+ val anno = EmittedFirrtlCircuitAnnotation(EmittedFirrtlCircuit(a.circuit.name, emitted, ".fir"))
+ Seq(DeletedAnnotation(name, anno), a)
+ case a => Some(a)
+ }
+ }
+
+}
diff --git a/src/main/scala/chisel3/stage/phases/MaybeFirrtlStage.scala b/src/main/scala/chisel3/stage/phases/MaybeFirrtlStage.scala
new file mode 100644
index 00000000..f830c182
--- /dev/null
+++ b/src/main/scala/chisel3/stage/phases/MaybeFirrtlStage.scala
@@ -0,0 +1,19 @@
+// See LICENSE for license details.
+
+package chisel3.stage.phases
+
+import chisel3.stage.NoRunFirrtlCompilerAnnotation
+
+import firrtl.AnnotationSeq
+import firrtl.options.Phase
+import firrtl.stage.FirrtlStage
+
+/** Run [[firrtl.stage.FirrtlStage]] if a [[chisel3.stage.NoRunFirrtlCompilerAnnotation]] is not present.
+ */
+class MaybeFirrtlStage extends Phase {
+
+ def transform(annotations: AnnotationSeq): AnnotationSeq = annotations
+ .collectFirst { case NoRunFirrtlCompilerAnnotation => annotations }
+ .getOrElse { (new FirrtlStage).transform(annotations) }
+
+}
diff --git a/src/test/scala/chisel3/stage/phases/DriverCompatibilitySpec.scala b/src/test/scala/chisel3/stage/phases/DriverCompatibilitySpec.scala
new file mode 100644
index 00000000..c478db27
--- /dev/null
+++ b/src/test/scala/chisel3/stage/phases/DriverCompatibilitySpec.scala
@@ -0,0 +1,70 @@
+// See LICENSE for license details.
+
+package chisel3.stage.phases
+
+import org.scalatest.{FlatSpec, Matchers}
+
+import chisel3.stage.{NoRunFirrtlCompilerAnnotation, ChiselOutputFileAnnotation}
+
+import firrtl.options.{OutputAnnotationFileAnnotation, StageOptions}
+import firrtl.options.Viewer.view
+import firrtl.stage.phases.DriverCompatibility.TopNameAnnotation
+
+class DriverCompatibilitySpec extends FlatSpec with Matchers {
+
+ behavior of classOf[DriverCompatibility.AddImplicitOutputFile].toString
+
+ it should "do nothing if a ChiselOutputFileAnnotation is present" in {
+ val annotations = Seq(
+ ChiselOutputFileAnnotation("Foo"),
+ TopNameAnnotation("Bar") )
+ (new DriverCompatibility.AddImplicitOutputFile).transform(annotations).toSeq should be (annotations)
+ }
+
+ it should "add a ChiselOutputFileAnnotation derived from a TopNameAnnotation" in {
+ val annotations = Seq( TopNameAnnotation("Bar") )
+ val expected = ChiselOutputFileAnnotation("Bar") +: annotations
+ (new DriverCompatibility.AddImplicitOutputFile).transform(annotations).toSeq should be (expected)
+ }
+
+ behavior of classOf[DriverCompatibility.AddImplicitOutputAnnotationFile].toString
+
+ it should "do nothing if an OutputAnnotationFileAnnotation is present" in {
+ val annotations = Seq(
+ OutputAnnotationFileAnnotation("Foo"),
+ TopNameAnnotation("Bar") )
+ (new DriverCompatibility.AddImplicitOutputAnnotationFile).transform(annotations).toSeq should be (annotations)
+ }
+
+ it should "add an OutputAnnotationFileAnnotation derived from a TopNameAnnotation" in {
+ val annotations = Seq( TopNameAnnotation("Bar") )
+ val expected = OutputAnnotationFileAnnotation("Bar") +: annotations
+ (new DriverCompatibility.AddImplicitOutputAnnotationFile).transform(annotations).toSeq should be (expected)
+ }
+
+ behavior of classOf[DriverCompatibility.DisableFirrtlStage].toString
+
+ it should "add a NoRunFirrtlCompilerAnnotation if one does not exist" in {
+ val annos = Seq(NoRunFirrtlCompilerAnnotation)
+ val expected = DriverCompatibility.RunFirrtlCompilerAnnotation +: annos
+ (new DriverCompatibility.DisableFirrtlStage).transform(Seq.empty).toSeq should be (expected)
+ }
+
+ it should "NOT add a NoRunFirrtlCompilerAnnotation if one already exists" in {
+ val annos = Seq(NoRunFirrtlCompilerAnnotation)
+ (new DriverCompatibility.DisableFirrtlStage).transform(annos).toSeq should be (annos)
+ }
+
+ behavior of classOf[DriverCompatibility.ReEnableFirrtlStage].toString
+
+ it should "NOT strip a NoRunFirrtlCompilerAnnotation if NO RunFirrtlCompilerAnnotation is present" in {
+ val annos = Seq(NoRunFirrtlCompilerAnnotation, DriverCompatibility.RunFirrtlCompilerAnnotation)
+ (new DriverCompatibility.ReEnableFirrtlStage).transform(annos).toSeq should be (Seq.empty)
+ }
+
+ it should "strip a NoRunFirrtlCompilerAnnotation if a RunFirrtlCompilerAnnotation is present" in {
+ val annos = Seq(NoRunFirrtlCompilerAnnotation)
+ (new DriverCompatibility.ReEnableFirrtlStage).transform(annos).toSeq should be (annos)
+ }
+
+}
diff --git a/src/test/scala/chiselTests/DriverSpec.scala b/src/test/scala/chiselTests/DriverSpec.scala
index 612bdef2..8fc58e21 100644
--- a/src/test/scala/chiselTests/DriverSpec.scala
+++ b/src/test/scala/chiselTests/DriverSpec.scala
@@ -28,6 +28,7 @@ class DriverSpec extends FreeSpec with Matchers {
val exts = List("anno.json", "fir", "v")
for (ext <- exts) {
val dummyOutput = new File(targetDir, "DummyModule" + "." + ext)
+ info(s"${dummyOutput.toString} exists")
dummyOutput.exists() should be(true)
dummyOutput.delete()
}
@@ -44,6 +45,7 @@ class DriverSpec extends FreeSpec with Matchers {
val exts = List("anno.json", "fir", "v")
for (ext <- exts) {
val dummyOutput = new File(targetDir, "dm" + "." + ext)
+ info(s"${dummyOutput.toString} exists")
dummyOutput.exists() should be(true)
dummyOutput.delete()
}
@@ -53,14 +55,21 @@ class DriverSpec extends FreeSpec with Matchers {
}
}
+
"execute returns a chisel execution result" in {
val targetDir = "test_run_dir"
val args = Array("--compiler", "low", "--target-dir", targetDir)
+
+ info("Driver returned a ChiselExecutionSuccess")
val result = Driver.execute(args, () => new DummyModule)
result shouldBe a[ChiselExecutionSuccess]
+
+ info("emitted circuit included 'circuit DummyModule'")
val successResult = result.asInstanceOf[ChiselExecutionSuccess]
successResult.emitted should include ("circuit DummyModule")
+
val dummyOutput = new File(targetDir, "DummyModule.lo.fir")
+ info(s"${dummyOutput.toString} exists")
dummyOutput.exists() should be(true)
dummyOutput.delete()
}
diff --git a/src/test/scala/chiselTests/stage/ChiselAnnotationsSpec.scala b/src/test/scala/chiselTests/stage/ChiselAnnotationsSpec.scala
new file mode 100644
index 00000000..c89955f2
--- /dev/null
+++ b/src/test/scala/chiselTests/stage/ChiselAnnotationsSpec.scala
@@ -0,0 +1,65 @@
+// See LICENSE for license details.
+
+package chiselTests.stage
+
+import org.scalatest.{FlatSpec, Matchers}
+
+import chisel3._
+import chisel3.stage.{ChiselCircuitAnnotation, ChiselGeneratorAnnotation}
+import chisel3.experimental.RawModule
+
+import firrtl.options.OptionsException
+
+class ChiselAnnotationsSpecFoo extends RawModule {
+ val in = IO(Input(Bool()))
+ val out = IO(Output(Bool()))
+ out := ~in
+}
+
+class ChiselAnnotationsSpecBaz(name: String) extends ChiselAnnotationsSpecFoo {
+ override val desiredName = name
+}
+
+class ChiselAnnotationsSpecQux extends ChiselAnnotationsSpecFoo {
+ /* This printf requires an implicit clock and reset, but RawModule has none. This will thereby fail elaboration. */
+ printf("hello")
+}
+
+class ChiselAnnotation
+
+class ChiselAnnotationsSpec extends FlatSpec with Matchers {
+
+ behavior of "ChiselGeneratorAnnotation elaboration"
+
+ it should "elaborate to a ChiselCircuitAnnotation" in {
+ val annotation = ChiselGeneratorAnnotation(() => new ChiselAnnotationsSpecFoo)
+ annotation.elaborate shouldBe a [ChiselCircuitAnnotation]
+ }
+
+ it should "throw an exception if elaboration fails" in {
+ val annotation = ChiselGeneratorAnnotation(() => new ChiselAnnotationsSpecQux)
+ intercept [ChiselException] { annotation.elaborate }
+ }
+
+ behavior of "ChiselGeneratorAnnotation when stringly constructing from Module names"
+
+ it should "elaborate from a String" in {
+ val annotation = ChiselGeneratorAnnotation("chiselTests.stage.ChiselAnnotationsSpecFoo")
+ annotation.elaborate shouldBe a [ChiselCircuitAnnotation]
+ }
+
+ it should "throw an exception if elaboration from a String refers to nonexistant class" in {
+ val bar = "chiselTests.stage.ChiselAnnotationsSpecBar"
+ val annotation = ChiselGeneratorAnnotation(bar)
+ intercept [OptionsException] { annotation.elaborate }
+ .getMessage should startWith (s"Unable to locate module '$bar'")
+ }
+
+ it should "throw an exception if elaboration from a String refers to an anonymous class" in {
+ val baz = "chiselTests.stage.ChiselAnnotationsSpecBaz"
+ val annotation = ChiselGeneratorAnnotation(baz)
+ intercept [OptionsException] { annotation.elaborate }
+ .getMessage should startWith (s"Unable to create instance of module '$baz'")
+ }
+
+}
diff --git a/src/test/scala/chiselTests/stage/ChiselOptionsViewSpec.scala b/src/test/scala/chiselTests/stage/ChiselOptionsViewSpec.scala
new file mode 100644
index 00000000..7dbeb9fa
--- /dev/null
+++ b/src/test/scala/chiselTests/stage/ChiselOptionsViewSpec.scala
@@ -0,0 +1,40 @@
+// See LICENSE for license details.
+
+package chiselTests.stage
+
+import org.scalatest.{FlatSpec, Matchers}
+
+import firrtl.options.Viewer.view
+
+import chisel3.stage._
+import chisel3.internal.firrtl.Circuit
+
+class ChiselOptionsViewSpec extends FlatSpec with Matchers {
+
+ behavior of ChiselOptionsView.getClass.getName
+
+ it should "construct a view from an AnnotationSeq" in {
+ val bar = Circuit("bar", Seq.empty, Seq.empty)
+ val annotations = Seq(
+ NoRunFirrtlCompilerAnnotation,
+ PrintFullStackTraceAnnotation,
+ ChiselOutputFileAnnotation("foo"),
+ ChiselCircuitAnnotation(bar)
+ )
+ val out = view[ChiselOptions](annotations)
+
+ info("runFirrtlCompiler was set to false")
+ out.runFirrtlCompiler should be (false)
+
+ info("printFullStackTrace was set to true")
+ out.printFullStackTrace should be (true)
+
+ info("outputFile was set to 'foo'")
+ out.outputFile should be (Some("foo"))
+
+ info("chiselCircuit was set to circuit 'bar'")
+ out.chiselCircuit should be (Some(bar))
+
+ }
+
+}
diff --git a/src/test/scala/chiselTests/stage/phases/AddImplicitOutputAnnotationFileSpec.scala b/src/test/scala/chiselTests/stage/phases/AddImplicitOutputAnnotationFileSpec.scala
new file mode 100644
index 00000000..f5fe0440
--- /dev/null
+++ b/src/test/scala/chiselTests/stage/phases/AddImplicitOutputAnnotationFileSpec.scala
@@ -0,0 +1,42 @@
+// See LICENSE for license details.
+
+package chiselTests.stage.phases
+
+import org.scalatest.{FlatSpec, Matchers}
+
+import chisel3.experimental.RawModule
+import chisel3.stage.ChiselGeneratorAnnotation
+import chisel3.stage.phases.{AddImplicitOutputAnnotationFile, Elaborate}
+
+import firrtl.AnnotationSeq
+import firrtl.options.{OutputAnnotationFileAnnotation, Phase}
+
+class AddImplicitOutputAnnotationFileSpec extends FlatSpec with Matchers {
+
+ class Foo extends RawModule { override val desiredName = "Foo" }
+
+ class Fixture { val phase: Phase = new AddImplicitOutputAnnotationFile }
+
+ behavior of classOf[AddImplicitOutputAnnotationFile].toString
+
+ it should "not override an existing OutputAnnotationFileAnnotation" in new Fixture {
+ val annotations: AnnotationSeq = Seq(
+ ChiselGeneratorAnnotation(() => new Foo),
+ OutputAnnotationFileAnnotation("Bar") )
+
+ Seq( new Elaborate, phase )
+ .foldLeft(annotations)((a, p) => p.transform(a))
+ .collect{ case a: OutputAnnotationFileAnnotation => a.file }
+ .toSeq should be (Seq("Bar"))
+ }
+
+ it should "generate an OutputAnnotationFileAnnotation from a ChiselCircuitAnnotation" in new Fixture {
+ val annotations: AnnotationSeq = Seq( ChiselGeneratorAnnotation(() => new Foo) )
+
+ Seq( new Elaborate, phase )
+ .foldLeft(annotations)((a, p) => p.transform(a))
+ .collect{ case a: OutputAnnotationFileAnnotation => a.file }
+ .toSeq should be (Seq("Foo"))
+ }
+
+}
diff --git a/src/test/scala/chiselTests/stage/phases/AddImplicitOutputFileSpec.scala b/src/test/scala/chiselTests/stage/phases/AddImplicitOutputFileSpec.scala
new file mode 100644
index 00000000..411aa6ba
--- /dev/null
+++ b/src/test/scala/chiselTests/stage/phases/AddImplicitOutputFileSpec.scala
@@ -0,0 +1,49 @@
+// See LICENSE for license details.
+
+package chiselTests.stage.phases
+
+import org.scalatest.{FlatSpec, Matchers}
+
+import chisel3.experimental.RawModule
+import chisel3.stage.{ChiselGeneratorAnnotation, ChiselOutputFileAnnotation}
+import chisel3.stage.phases.{AddImplicitOutputFile, Elaborate}
+
+import firrtl.AnnotationSeq
+import firrtl.options.{Phase, StageOptions, TargetDirAnnotation}
+import firrtl.options.Viewer.view
+
+class AddImplicitOutputFileSpec extends FlatSpec with Matchers {
+
+ class Foo extends RawModule { override val desiredName = "Foo" }
+
+ class Fixture { val phase: Phase = new AddImplicitOutputFile }
+
+ behavior of classOf[AddImplicitOutputFile].toString
+
+ it should "not override an existing ChiselOutputFileAnnotation" in new Fixture {
+ val annotations: AnnotationSeq = Seq(
+ ChiselGeneratorAnnotation(() => new Foo),
+ ChiselOutputFileAnnotation("Bar") )
+
+ Seq( new Elaborate, phase )
+ .foldLeft(annotations)((a, p) => p.transform(a))
+ .collect{ case a: ChiselOutputFileAnnotation => a.file }
+ .toSeq should be (Seq("Bar"))
+ }
+
+ it should "generate a ChiselOutputFileAnnotation from a ChiselCircuitAnnotation" in new Fixture {
+ val annotations: AnnotationSeq = Seq(
+ ChiselGeneratorAnnotation(() => new Foo),
+ TargetDirAnnotation("test_run_dir") )
+
+ Seq( new Elaborate, phase )
+ .foldLeft(annotations)((a, p) => p.transform(a))
+ .collect{ case a: ChiselOutputFileAnnotation => a.file }
+ .toSeq should be (Seq("Foo"))
+ }
+
+ it should "do nothing to an empty annotation sequence" in new Fixture {
+ phase.transform(AnnotationSeq(Seq.empty)).toSeq should be (empty)
+ }
+
+}
diff --git a/src/test/scala/chiselTests/stage/phases/ChecksSpec.scala b/src/test/scala/chiselTests/stage/phases/ChecksSpec.scala
new file mode 100644
index 00000000..6d01e38e
--- /dev/null
+++ b/src/test/scala/chiselTests/stage/phases/ChecksSpec.scala
@@ -0,0 +1,43 @@
+// See LICENSE for license details.
+
+package chiselTests.stage.phases
+
+import org.scalatest.{FlatSpec, Matchers}
+
+import chisel3.stage.{ChiselOutputFileAnnotation, NoRunFirrtlCompilerAnnotation, PrintFullStackTraceAnnotation}
+import chisel3.stage.phases.Checks
+
+import firrtl.AnnotationSeq
+import firrtl.annotations.NoTargetAnnotation
+import firrtl.options.{OptionsException, Phase}
+
+class ChecksSpec extends FlatSpec with Matchers {
+
+ def checkExceptionMessage(phase: Phase, annotations: AnnotationSeq, messageStart: String): Unit =
+ intercept[OptionsException]{ phase.transform(annotations) }.getMessage should startWith(messageStart)
+
+ class Fixture { val phase: Phase = new Checks }
+
+ behavior of classOf[Checks].toString
+
+ it should "do nothing on sane annotation sequences" in new Fixture {
+ val a = Seq(NoRunFirrtlCompilerAnnotation, PrintFullStackTraceAnnotation)
+ phase.transform(a).toSeq should be (a)
+ }
+
+ it should "throw an OptionsException if more than one NoRunFirrtlCompilerAnnotation is specified" in new Fixture {
+ val a = Seq(NoRunFirrtlCompilerAnnotation, NoRunFirrtlCompilerAnnotation)
+ checkExceptionMessage(phase, a, "At most one NoRunFirrtlCompilerAnnotation")
+ }
+
+ it should "throw an OptionsException if more than one PrintFullStackTraceAnnotation is specified" in new Fixture {
+ val a = Seq(PrintFullStackTraceAnnotation, PrintFullStackTraceAnnotation)
+ checkExceptionMessage(phase, a, "At most one PrintFullStackTraceAnnotation")
+ }
+
+ it should "throw an OptionsException if more than one ChiselOutputFileAnnotation is specified" in new Fixture {
+ val a = Seq(ChiselOutputFileAnnotation("foo"), ChiselOutputFileAnnotation("bar"))
+ checkExceptionMessage(phase, a, "At most one Chisel output file")
+ }
+
+}
diff --git a/src/test/scala/chiselTests/stage/phases/ConvertSpec.scala b/src/test/scala/chiselTests/stage/phases/ConvertSpec.scala
new file mode 100644
index 00000000..30fad4f5
--- /dev/null
+++ b/src/test/scala/chiselTests/stage/phases/ConvertSpec.scala
@@ -0,0 +1,62 @@
+// See LICENSE for license details.
+
+package chiselTests.stage.phases
+
+import org.scalatest.{FlatSpec, Matchers}
+
+import chisel3._
+import chisel3.experimental.{ChiselAnnotation, RawModule, RunFirrtlTransform}
+import chisel3.stage.ChiselGeneratorAnnotation
+import chisel3.stage.phases.{Convert, Elaborate}
+
+import firrtl.{AnnotationSeq, CircuitForm, CircuitState, Transform, UnknownForm}
+import firrtl.annotations.{Annotation, NoTargetAnnotation}
+import firrtl.options.Phase
+import firrtl.stage.{FirrtlCircuitAnnotation, RunFirrtlTransformAnnotation}
+
+class ConvertSpecFirrtlTransform extends Transform {
+ def inputForm: CircuitForm = UnknownForm
+ def outputForm: CircuitForm = UnknownForm
+ def execute(state: CircuitState): CircuitState = state
+}
+
+case class ConvertSpecFirrtlAnnotation(name: String) extends NoTargetAnnotation
+
+case class ConvertSpecChiselAnnotation(name: String) extends ChiselAnnotation with RunFirrtlTransform {
+ def toFirrtl: Annotation = ConvertSpecFirrtlAnnotation(name)
+ def transformClass: Class[_ <: Transform] = classOf[ConvertSpecFirrtlTransform]
+}
+
+class ConvertSpecFoo extends RawModule {
+ override val desiredName: String = "foo"
+
+ val in = IO(Input(Bool()))
+ val out = IO(Output(Bool()))
+
+ experimental.annotate(ConvertSpecChiselAnnotation("bar"))
+}
+
+class ConvertSpec extends FlatSpec with Matchers {
+
+ class Fixture { val phase: Phase = new Convert }
+
+ behavior of classOf[Convert].toString
+
+ it should "convert a Chisel Circuit to a FIRRTL Circuit" in new Fixture {
+ val annos: AnnotationSeq = Seq(ChiselGeneratorAnnotation(() => new ConvertSpecFoo))
+
+ val annosx = Seq(new Elaborate, phase)
+ .foldLeft(annos)( (a, p) => p.transform(a) )
+
+ info("FIRRTL circuit generated")
+ annosx.collect{ case a: FirrtlCircuitAnnotation => a.circuit.main }.toSeq should be (Seq("foo"))
+
+ info("FIRRTL annotations generated")
+ annosx.collect{ case a: ConvertSpecFirrtlAnnotation => a.name }.toSeq should be (Seq("bar"))
+
+ info("FIRRTL transform annotations generated")
+ annosx.collect{ case a: RunFirrtlTransformAnnotation => a.transform.getClass}
+ .toSeq should be (Seq(classOf[ConvertSpecFirrtlTransform]))
+ }
+
+}
diff --git a/src/test/scala/chiselTests/stage/phases/ElaborateSpec.scala b/src/test/scala/chiselTests/stage/phases/ElaborateSpec.scala
new file mode 100644
index 00000000..4d99b24c
--- /dev/null
+++ b/src/test/scala/chiselTests/stage/phases/ElaborateSpec.scala
@@ -0,0 +1,46 @@
+// See LICENSE for license details.
+
+package chiselTests.stage.phases
+
+import org.scalatest.{FlatSpec, Matchers}
+
+import chisel3._
+import chisel3.stage.{ChiselCircuitAnnotation, ChiselGeneratorAnnotation}
+import chisel3.stage.phases.Elaborate
+
+import firrtl.options.Phase
+
+class ElaborateSpec extends FlatSpec with Matchers {
+
+ class Foo extends Module {
+ override def desiredName: String = "Foo"
+ val io = IO(
+ new Bundle {
+ val in = Input(Bool())
+ val out = Output(Bool())
+ })
+
+ io.out := ~io.in
+ }
+
+ class Bar extends Foo {
+ override def desiredName: String = "Bar"
+ }
+
+ class Fixture { val phase: Phase = new Elaborate }
+
+ behavior of classOf[Elaborate].toString
+
+ it should "expand ChiselGeneratorAnnotations into ChiselCircuitAnnotations and delete originals" in new Fixture {
+ val annotations = Seq( ChiselGeneratorAnnotation(() => new Foo),
+ ChiselGeneratorAnnotation(() => new Bar) )
+ val out = phase.transform(annotations)
+
+ info("original annotations removed")
+ out.collect{ case a: ChiselGeneratorAnnotation => a } should be (empty)
+
+ info("circuits created with the expected names")
+ out.collect{ case a: ChiselCircuitAnnotation => a.circuit.name } should be (Seq("Foo", "Bar"))
+ }
+
+}
diff --git a/src/test/scala/chiselTests/stage/phases/EmitterSpec.scala b/src/test/scala/chiselTests/stage/phases/EmitterSpec.scala
new file mode 100644
index 00000000..63498adb
--- /dev/null
+++ b/src/test/scala/chiselTests/stage/phases/EmitterSpec.scala
@@ -0,0 +1,60 @@
+// See LICENSE for license details.
+
+package chiselTests.stage.phases
+
+import org.scalatest.{FlatSpec, Matchers}
+
+import chisel3.experimental.RawModule
+import chisel3.stage.{ChiselCircuitAnnotation, ChiselGeneratorAnnotation, ChiselOutputFileAnnotation}
+import chisel3.stage.phases.{Convert, Elaborate, Emitter}
+
+import firrtl.{AnnotationSeq, EmittedFirrtlCircuitAnnotation}
+import firrtl.annotations.DeletedAnnotation
+import firrtl.options.{Phase, TargetDirAnnotation}
+
+import java.io.File
+
+class EmitterSpec extends FlatSpec with Matchers {
+
+ class FooModule extends RawModule { override val desiredName = "Foo" }
+ class BarModule extends RawModule { override val desiredName = "Bar" }
+
+ class Fixture { val phase: Phase = new Emitter }
+
+ behavior of classOf[Emitter].toString
+
+ it should "do nothing if no ChiselOutputFileAnnotations are present" in new Fixture {
+ val dir = new File("test_run_dir/EmitterSpec")
+ val annotations = (new Elaborate).transform(Seq( TargetDirAnnotation(dir.toString),
+ ChiselGeneratorAnnotation(() => new FooModule) ))
+ val annotationsx = phase.transform(annotations)
+
+ val Seq(fooFile, barFile) = Seq("Foo.fir", "Bar.fir").map(f => new File(dir + "/" + f))
+
+ info(s"$fooFile does not exist")
+ fooFile should not (exist)
+
+ info("annotations are unmodified")
+ annotationsx.toSeq should be (annotations.toSeq)
+ }
+
+ it should "emit a ChiselCircuitAnnotation to a specific file" in new Fixture {
+ val dir = new File("test_run_dir/EmitterSpec")
+ val circuit = (new Elaborate)
+ .transform(Seq(ChiselGeneratorAnnotation(() => new BarModule)))
+ .collectFirst{ case a: ChiselCircuitAnnotation => a}
+ .get
+ val annotations = phase.transform(Seq( TargetDirAnnotation(dir.toString),
+ circuit,
+ ChiselOutputFileAnnotation("Baz") ))
+
+ val bazFile = new File(dir + "/Baz.fir")
+
+ info(s"$bazFile exists")
+ bazFile should (exist)
+
+ info("a deleted EmittedFirrtlCircuitAnnotation should be generated")
+ annotations.collect{ case a @ DeletedAnnotation(_, _: EmittedFirrtlCircuitAnnotation) => a }.size should be (1)
+ }
+
+}