From a423db5fa5a9fb106c1b0048b7dcf97fc83953b6 Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Thu, 10 Jan 2019 12:47:18 -0500 Subject: Add chisel3.stage Annotations This adds the following FIRRTL Annotations to Chisel: - NoRunFirrtlCompilerAnnotation - PrintFullStackTraceAnnotation - ChiselGeneratorAnnotation - ChiselCircuitAnnotation - ChiselOutputFileAnnotation This includes tests for ChiselGeneratorAnnotation as this Annotation is able to be constructed from a String and to elaborate itself. Co-Authored-By: Schuyler Eldridge Co-Authored-By: chick Signed-off-by: Schuyler Eldridge --- .../scala/chisel3/stage/ChiselAnnotations.scala | 105 +++++++++++++++++++++ .../chiselTests/stage/ChiselAnnotationsSpec.scala | 65 +++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 src/main/scala/chisel3/stage/ChiselAnnotations.scala create mode 100644 src/test/scala/chiselTests/stage/ChiselAnnotationsSpec.scala (limited to 'src') 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(".") ) ) + +} + +/** 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: .fir)", + helpValueName = Some("") ) ) + +} 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'") + } + +} -- cgit v1.2.3 From 0a8ffcb856da4007d1a39503c629295bee42acfb Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Thu, 10 Jan 2019 12:47:37 -0500 Subject: Add chisel3.stage.ChiselCli Co-Authored-By: Schuyler Eldridge Co-Authored-By: chick Signed-off-by: Schuyler Eldridge --- src/main/scala/chisel3/stage/ChiselCli.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/scala/chisel3/stage/ChiselCli.scala (limited to 'src') 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)) +} -- cgit v1.2.3 From f592422fc1dcc374139bbeb84a814134c5e58ac4 Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Thu, 10 Jan 2019 12:52:00 -0500 Subject: Add chisel3.stage.ChiselOptions Signed-off-by: Schuyler Eldridge --- src/main/scala/chisel3/stage/ChiselOptions.scala | 27 ++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/main/scala/chisel3/stage/ChiselOptions.scala (limited to 'src') 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 ) + + } + +} -- cgit v1.2.3 From 06c5e3e82f7dd4aa8ce159aa4c13b9bc36abce96 Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Fri, 1 Feb 2019 03:17:15 -0500 Subject: Add ChiselOptionsView Co-Authored-By: Schuyler Eldridge Co-Authored-By: chick Signed-off-by: Schuyler Eldridge --- src/main/scala/chisel3/stage/package.scala | 25 ++++++++++++++ .../chiselTests/stage/ChiselOptionsViewSpec.scala | 40 ++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 src/main/scala/chisel3/stage/package.scala create mode 100644 src/test/scala/chiselTests/stage/ChiselOptionsViewSpec.scala (limited to 'src') diff --git a/src/main/scala/chisel3/stage/package.scala b/src/main/scala/chisel3/stage/package.scala new file mode 100644 index 00000000..851dd400 --- /dev/null +++ b/src/main/scala/chisel3/stage/package.scala @@ -0,0 +1,25 @@ +// See LICENSE for license details. + +package chisel3 + +import firrtl._ +import firrtl.options.OptionsView + +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)) + } + } + + } + +} 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)) + + } + +} -- cgit v1.2.3 From f447c2253081f7c2ede0658d059bc00c184312f9 Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Thu, 10 Jan 2019 13:07:21 -0500 Subject: Add chisel3.stage.phases.Checks Phase Signed-off-by: Schuyler Eldridge --- src/main/scala/chisel3/stage/phases/Checks.scala | 49 ++++++++++++++++++++++ .../chiselTests/stage/phases/ChecksSpec.scala | 43 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 src/main/scala/chisel3/stage/phases/Checks.scala create mode 100644 src/test/scala/chiselTests/stage/phases/ChecksSpec.scala (limited to 'src') 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/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") + } + +} -- cgit v1.2.3 From 4c48d5a94f9242f471e4c1ad39c664c672eafe13 Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Wed, 30 Jan 2019 12:06:00 -0500 Subject: Add chisel3.stage.phases.Elaborate Phase This adds an Elaborate Phase that expands ChiselGeneratorAnnotations into ChiselCircuitAnnotations and deletes the original. Co-Authored-By: Schuyler Eldridge Co-Authored-By: chick Signed-off-by: Schuyler Eldridge --- .../scala/chisel3/stage/phases/Elaborate.scala | 42 ++++++++++++++++++++ .../chiselTests/stage/phases/ElaborateSpec.scala | 46 ++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 src/main/scala/chisel3/stage/phases/Elaborate.scala create mode 100644 src/test/scala/chiselTests/stage/phases/ElaborateSpec.scala (limited to 'src') 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/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")) + } + +} -- cgit v1.2.3 From 325e48809587fdf47d398578a1d94f856ab1f275 Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Fri, 11 Jan 2019 15:55:59 -0500 Subject: Add chisel3.stage.phases.Convert Phase This coalesces three distinct operations into one Convert Phase: 1. Chisel Circuit to FIRRTL Circuit (CHIRRTL) conversion 2. Conversion of Chisel Annotations to FIRRTL Annotations 3. Generation of RunFirrtlTransformAnnotations Co-Authored-By: Schuyler Eldridge Co-Authored-By: chick Signed-off-by: Schuyler Eldridge --- src/main/scala/chisel3/stage/phases/Convert.scala | 41 ++++++++++++++ .../chiselTests/stage/phases/ConvertSpec.scala | 62 ++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 src/main/scala/chisel3/stage/phases/Convert.scala create mode 100644 src/test/scala/chiselTests/stage/phases/ConvertSpec.scala (limited to 'src') 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/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])) + } + +} -- cgit v1.2.3 From 20ba486ab1988e57e2b2ca163c9c83e1d8904bba Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Mon, 14 Jan 2019 11:59:48 -0500 Subject: Add chisel3.stage.phases.Emitter Phase This adds an Emitter Phase that writes a ChiselCircuitAnnotation to a file if a ChiselOutputFileAnnotation is present. Signed-off-by: Schuyler Eldridge --- src/main/scala/chisel3/stage/phases/Emitter.scala | 44 ++++++++++++++++ .../chiselTests/stage/phases/EmitterSpec.scala | 60 ++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 src/main/scala/chisel3/stage/phases/Emitter.scala create mode 100644 src/test/scala/chiselTests/stage/phases/EmitterSpec.scala (limited to 'src') 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/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) + } + +} -- cgit v1.2.3 From 0e6eb5b35a442edf70ad37f963526609f2ba1f3c Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Mon, 14 Jan 2019 12:20:44 -0500 Subject: Add chisel.stage.phases.AddImplicitOutputFile Signed-off-by: Schuyler Eldridge --- .../stage/phases/AddImplicitOutputFile.scala | 25 +++++++++++ .../stage/phases/AddImplicitOutputFileSpec.scala | 49 ++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 src/main/scala/chisel3/stage/phases/AddImplicitOutputFile.scala create mode 100644 src/test/scala/chiselTests/stage/phases/AddImplicitOutputFileSpec.scala (limited to 'src') 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/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) + } + +} -- cgit v1.2.3 From 121e3d87598f2056b76846472970620d046c2487 Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Mon, 14 Jan 2019 14:29:46 -0500 Subject: Add stage.phases.AddImplicitOutputAnnotationFile Co-Authored-By: Schuyler Eldridge Co-Authored-By: chick Signed-off-by: Schuyler Eldridge --- .../phases/AddImplicitOutputAnnotationFile.scala | 25 +++++++++++++ .../AddImplicitOutputAnnotationFileSpec.scala | 42 ++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 src/main/scala/chisel3/stage/phases/AddImplicitOutputAnnotationFile.scala create mode 100644 src/test/scala/chiselTests/stage/phases/AddImplicitOutputAnnotationFileSpec.scala (limited to 'src') 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/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")) + } + +} -- cgit v1.2.3 From 0ac473b5f80b9627a06fc5caa052899680fd13cb Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Fri, 3 May 2019 13:00:05 -0400 Subject: Add chisel3.stage.phases.MaybeFirrtlStage Signed-off-by: Schuyler Eldridge --- .../scala/chisel3/stage/phases/MaybeFirrtlStage.scala | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/main/scala/chisel3/stage/phases/MaybeFirrtlStage.scala (limited to 'src') 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) } + +} -- cgit v1.2.3 From 1abb84497315cff795e24afda0e4790fe535132f Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Mon, 14 Jan 2019 21:01:16 -0500 Subject: Add chisel3.stage.ChiselStage This adds ChiselStage, a reimplementation of chisel3.Driver as a firrtl.options.Stage. This is simplistically described as a pipeline of Phases. Co-Authored-By: Schuyler Eldridge Co-Authored-By: chick Signed-off-by: Schuyler Eldridge --- src/main/scala/chisel3/stage/ChiselStage.scala | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/main/scala/chisel3/stage/ChiselStage.scala (limited to 'src') 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) ) + +} -- cgit v1.2.3 From 41482912bd2216d4f95043414798120536398e54 Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Mon, 14 Jan 2019 12:35:02 -0500 Subject: Add Driver Compatibility Layer This includes phases necessary to provide backwards compatibility with the old Chisel3 Driver. These are placed in a DriverCompatibility object inside chisel3.stage.phases. The following four phases are included: - AddImplicitOutputFile (from a TopNameAnnotation) - AddImplicitOutputAnnotationFile phase - DisableFirrtlStage (to disable ChiselStage running FirrtlStage) - MutateOptionsManager (to update options after ChiselStage) - ReEnableFirrtlStage (to renable FirrtlStage if needed) Additionally, this adds a view of a ChiselExecutionResult for providing the legacy return type of the Chisel Driver. Co-Authored-By: Schuyler Eldridge Co-Authored-By: chick Signed-off-by: Schuyler Eldridge --- src/main/scala/chisel3/stage/package.scala | 33 ++++++ .../chisel3/stage/phases/DriverCompatibility.scala | 115 +++++++++++++++++++++ .../stage/phases/DriverCompatibilitySpec.scala | 70 +++++++++++++ 3 files changed, 218 insertions(+) create mode 100644 src/main/scala/chisel3/stage/phases/DriverCompatibility.scala create mode 100644 src/test/scala/chisel3/stage/phases/DriverCompatibilitySpec.scala (limited to 'src') diff --git a/src/main/scala/chisel3/stage/package.scala b/src/main/scala/chisel3/stage/package.scala index 851dd400..67d38ae7 100644 --- a/src/main/scala/chisel3/stage/package.scala +++ b/src/main/scala/chisel3/stage/package.scala @@ -3,8 +3,12 @@ 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] { @@ -22,4 +26,33 @@ package object stage { } + 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/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/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) + } + +} -- cgit v1.2.3 From 07ea0bac73d2ee5c9d09e9f3c07275340f0e75bb Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Fri, 11 Jan 2019 14:33:43 -0500 Subject: Add toAnnotations method to ChiselExecutionOptions Adds a method to enable conversion from ChiselExecutionOptions back to an AnnotationSeq. Signed-off-by: Schuyler Eldridge --- src/main/scala/chisel3/ChiselExecutionOptions.scala | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src') 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 => -- cgit v1.2.3 From 23641521174ba828feb32dc1c65c13a3d6b46970 Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Mon, 14 Jan 2019 21:01:29 -0500 Subject: Make Driver a ChiselStage compatibility layer This converts the original chisel3.Driver to use chisel3.stage.ChiselStage. This is implemented in the following way: 1. ExecutionOptions are converted to an AnnotationSeq 2. The AnnotationSeq is preprocessed using phases contained in the Chisel DriverCompatibility objects. One of these *disables* the execution of FirrtlStage by ChiselStage. 3. ChiselStage runs on the preprocessed AnnotationSeq 4. The input ExecutionOptionsManager is mutated based on the output of ChiselStage. 5. The FIRRTL stage is re-enabled if it's supposed to run and selected FIRRTL DriverCompatibility phases run. 6. FirrtlStage runs 7. The output AnnotationSeq is "viewed" as a ChiselExecutionResult This modifies the original DriverSpec to make it more verbose with the addition of info statements. The functionality of the DriverSpec is unmodified. Signed-off-by: Schuyler Eldridge --- src/main/scala/chisel3/Driver.scala | 80 ++++++++++------------------- src/test/scala/chiselTests/DriverSpec.scala | 9 ++++ 2 files changed, 35 insertions(+), 54 deletions(-) (limited to 'src') 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/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() } -- cgit v1.2.3