diff options
| author | Chick Markley | 2019-06-03 16:22:43 -0700 |
|---|---|---|
| committer | GitHub | 2019-06-03 16:22:43 -0700 |
| commit | 821fe17b110e2a9017a335516227cb491c18cf43 (patch) | |
| tree | 22fe46c8f675ca3739f893890efc2479f367d9a1 /src/main | |
| parent | 820e2688f0cb966d4528ff074fdbdc623ed8f940 (diff) | |
| parent | 23641521174ba828feb32dc1c65c13a3d6b46970 (diff) | |
Merge pull request #1004 from freechipsproject/chisel-stage
Chisel stage
Diffstat (limited to 'src/main')
| -rw-r--r-- | src/main/scala/chisel3/ChiselExecutionOptions.scala | 12 | ||||
| -rw-r--r-- | src/main/scala/chisel3/Driver.scala | 80 | ||||
| -rw-r--r-- | src/main/scala/chisel3/stage/ChiselAnnotations.scala | 105 | ||||
| -rw-r--r-- | src/main/scala/chisel3/stage/ChiselCli.scala | 12 | ||||
| -rw-r--r-- | src/main/scala/chisel3/stage/ChiselOptions.scala | 27 | ||||
| -rw-r--r-- | src/main/scala/chisel3/stage/ChiselStage.scala | 26 | ||||
| -rw-r--r-- | src/main/scala/chisel3/stage/package.scala | 58 | ||||
| -rw-r--r-- | src/main/scala/chisel3/stage/phases/AddImplicitOutputAnnotationFile.scala | 25 | ||||
| -rw-r--r-- | src/main/scala/chisel3/stage/phases/AddImplicitOutputFile.scala | 25 | ||||
| -rw-r--r-- | src/main/scala/chisel3/stage/phases/Checks.scala | 49 | ||||
| -rw-r--r-- | src/main/scala/chisel3/stage/phases/Convert.scala | 41 | ||||
| -rw-r--r-- | src/main/scala/chisel3/stage/phases/DriverCompatibility.scala | 115 | ||||
| -rw-r--r-- | src/main/scala/chisel3/stage/phases/Elaborate.scala | 42 | ||||
| -rw-r--r-- | src/main/scala/chisel3/stage/phases/Emitter.scala | 44 | ||||
| -rw-r--r-- | src/main/scala/chisel3/stage/phases/MaybeFirrtlStage.scala | 19 |
15 files changed, 624 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) } + +} |
