Skip to content

Commit

Permalink
Merge pull request #1004 from freechipsproject/chisel-stage
Browse files Browse the repository at this point in the history
Chisel stage
  • Loading branch information
chick authored Jun 3, 2019
2 parents 820e268 + 2364152 commit 821fe17
Show file tree
Hide file tree
Showing 25 changed files with 1,110 additions and 56 deletions.
12 changes: 10 additions & 2 deletions src/main/scala/chisel3/ChiselExecutionOptions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 =>
Expand Down
80 changes: 26 additions & 54 deletions src/main/scala/chisel3/Driver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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}

/**
Expand Down Expand Up @@ -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) {
Expand All @@ -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)
}

/**
Expand Down
105 changes: 105 additions & 0 deletions src/main/scala/chisel3/stage/ChiselAnnotations.scala
Original file line number Diff line number Diff line change
@@ -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>") ) )

}
12 changes: 12 additions & 0 deletions src/main/scala/chisel3/stage/ChiselCli.scala
Original file line number Diff line number Diff line change
@@ -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))
}
27 changes: 27 additions & 0 deletions src/main/scala/chisel3/stage/ChiselOptions.scala
Original file line number Diff line number Diff line change
@@ -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 )

}

}
26 changes: 26 additions & 0 deletions src/main/scala/chisel3/stage/ChiselStage.scala
Original file line number Diff line number Diff line change
@@ -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) )

}
58 changes: 58 additions & 0 deletions src/main/scala/chisel3/stage/package.scala
Original file line number Diff line number Diff line change
@@ -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))
}

}

}

}
Original file line number Diff line number Diff line change
@@ -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)
}

}
Loading

0 comments on commit 821fe17

Please sign in to comment.