Skip to content

Commit

Permalink
Add --dump-fir option to ChiselStage (backport #3453) (#3456)
Browse files Browse the repository at this point in the history
* Add --dump-fir option to ChiselStage (#3453)

This option will dump the .fir before invoking firtool.

Additional changes:
* Use os.lib for invoking firtool
* Use lazy serialization to avoid holding the entire FIRRTL in
  memory.
* Mix NoStackTrace into FirtoolNotFound
* Fix detection of no firtool

(cherry picked from commit 4db86b2)

# Conflicts:
#	src/main/scala/circt/stage/CIRCTOptions.scala
#	src/main/scala/circt/stage/ChiselStage.scala
#	src/main/scala/circt/stage/phases/CIRCT.scala

* Resolve backport conflicts

---------

Co-authored-by: Jack Koenig <koenig@sifive.com>
  • Loading branch information
mergify[bot] and jackkoenig authored Nov 16, 2023
1 parent 1de1174 commit 066fb47
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 13 deletions.
14 changes: 14 additions & 0 deletions src/main/scala/circt/stage/Annotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,17 @@ private[circt] case object SplitVerilog extends NoTargetAnnotation with CIRCTOpt
)

}

/** Write the intermediate `.fir` file in [[circt.stage.ChiselStage]]
*/
private[circt] case object DumpFir extends NoTargetAnnotation with CIRCTOption with HasShellOptions {
override def options = Seq(
new ShellOption[Unit](
longOption = "dump-fir",
toAnnotationSeq = _ => Seq(this),
helpText = "Write the intermediate .fir file",
helpValueName = None
)
)

}
10 changes: 7 additions & 3 deletions src/main/scala/circt/stage/CIRCTOptions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,24 @@ import java.io.File
* @param outputFile the name of the file where the result will be written, if not split
* @param preserveAggregate causes CIRCT to not lower aggregate FIRRTL IR types
* @param target the specific IR or language target that CIRCT should compile to
* @param dumpFir dump the intermediate .fir artifact
*/
class CIRCTOptions private[stage] (
val outputFile: Option[File] = None,
val preserveAggregate: Option[PreserveAggregate.Type] = None,
val target: Option[CIRCTTarget.Type] = None,
val firtoolOptions: Seq[String] = Seq.empty,
val splitVerilog: Boolean = false) {
val splitVerilog: Boolean = false,
val dumpFir: Boolean = false) {

private[stage] def copy(
outputFile: Option[File] = outputFile,
preserveAggregate: Option[PreserveAggregate.Type] = preserveAggregate,
target: Option[CIRCTTarget.Type] = target,
firtoolOptions: Seq[String] = firtoolOptions,
splitVerilog: Boolean = splitVerilog
): CIRCTOptions = new CIRCTOptions(outputFile, preserveAggregate, target, firtoolOptions, splitVerilog)
splitVerilog: Boolean = splitVerilog,
dumpFir: Boolean = dumpFir
): CIRCTOptions =
new CIRCTOptions(outputFile, preserveAggregate, target, firtoolOptions, splitVerilog, dumpFir)

}
3 changes: 2 additions & 1 deletion src/main/scala/circt/stage/ChiselStage.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ trait CLI { this: BareShell =>
WarningConfigurationAnnotation,
WarningConfigurationFileAnnotation,
SourceRootAnnotation,
SplitVerilog
SplitVerilog,
DumpFir
).foreach(_.addOptions(parser))
}

Expand Down
1 change: 1 addition & 0 deletions src/main/scala/circt/stage/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ package object stage {
case PreserveAggregate(a) => acc.copy(preserveAggregate = Some(a))
case FirtoolOption(a) => acc.copy(firtoolOptions = acc.firtoolOptions :+ a)
case SplitVerilog => acc.copy(splitVerilog = true)
case DumpFir => acc.copy(dumpFir = true)
case _ => acc
}
}
Expand Down
39 changes: 30 additions & 9 deletions src/main/scala/circt/stage/phases/CIRCT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ private[this] object Exceptions {
| https://github.com/llvm/circt/releases""".stripMargin
)
)
with NoStackTrace

}

Expand Down Expand Up @@ -181,20 +182,35 @@ class CIRCT extends Phase {
/* Filter the annotations to only those things which CIRCT should see. */
(new WriteOutputAnnotations).transform(annotationsx)

val input: String = firrtlOptions.firrtlCircuit match {
case None => throw new OptionsException("No input file specified!")
case Some(circuit) => circuit.serialize
val (serialization: Iterable[String], circuitName: String) = firrtlOptions.firrtlCircuit match {
case None => throw new OptionsException("No input file specified!")
// TODO can we avoid converting, how else would we include filteredAnnos?
case Some(circuit) =>
(firrtl.ir.Serializer.lazily(circuit), circuit.main)
}

// FIRRTL is serialized either in memory or to a file
val input: Either[Iterable[String], os.Path] =
if (circtOptions.dumpFir) {
val td = os.Path(stageOptions.targetDir, os.pwd)
val filename = firrtlOptions.outputFileName.getOrElse(circuitName)
val firPath = td / s"$filename.fir"
os.write.over(firPath, serialization, createFolders = true)
Right(firPath)
} else {
Left(serialization)
}

val chiselAnnotationFilename: Option[String] =
stageOptions.annotationFileOut.map(stageOptions.getBuildFileName(_, Some(".anno.json")))

val circtAnnotationFilename = "circt.anno.json"

val binary = "firtool"

val cmd =
Seq(binary, "-format=fir", "-warn-on-unprocessed-annotations", "-dedup") ++
val cmd = // Only 1 of input or firFile will be Some
Seq(binary, input.fold(_ => "-format=fir", _.toString)) ++
Seq("-warn-on-unprocessed-annotations", "-dedup") ++
Seq("-output-annotation-file", circtAnnotationFilename) ++
circtOptions.firtoolOptions ++
logLevel.toCIRCTOptions ++
Expand Down Expand Up @@ -231,16 +247,21 @@ class CIRCT extends Phase {
)
})

logger.info(s"""Running CIRCT: '${cmd.mkString(" ")} < $$input'""")
logger.info(s"""Running CIRCT: '${cmd.mkString(" ")}""" + input.fold(_ => " < $$input'", _ => "'"))
val stdoutStream, stderrStream = new java.io.ByteArrayOutputStream
val stdoutWriter = new java.io.PrintWriter(stdoutStream)
val stderrWriter = new java.io.PrintWriter(stderrStream)
val stdin: os.ProcessInput = input match {
case Left(it) => (it: os.Source) // Static cast to apply implicit conversion
case Right(_) => os.Pipe
}
val stdout = os.ProcessOutput.Readlines(stdoutWriter.println)
val stderr = os.ProcessOutput.Readlines(stderrWriter.println)
val exitValue =
try {
(cmd #< new java.io.ByteArrayInputStream(input.getBytes))
.!(ProcessLogger(stdoutWriter.println, stderrWriter.println))
os.proc(cmd).call(check = false, stdin = stdin, stdout = stdout, stderr = stderr).exitCode
} catch {
case a: java.lang.RuntimeException if a.getMessage().startsWith("No exit code") =>
case a: java.io.IOException if a.getMessage().startsWith("Cannot run program") =>
throw new Exceptions.FirtoolNotFound(binary)
}
stdoutWriter.close()
Expand Down
28 changes: 28 additions & 0 deletions src/test/scala/circtTests/stage/ChiselStageSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,34 @@ class ChiselStageSpec extends AnyFunSpec with Matchers with chiselTests.Utils {

}

it("should optionally emit .fir when compiling to SystemVerilog") {

val targetDir = new File("test_run_dir/ChiselStageSpec")

val args: Array[String] = Array(
"--target",
"systemverilog",
"--target-dir",
targetDir.toString,
"--dump-fir"
)

val expectedSV = new File(targetDir, "Foo.sv")
expectedSV.delete()

val expectedFir = new File(targetDir, "Foo.fir")
expectedFir.delete()

(new ChiselStage)
.execute(args, Seq(ChiselGeneratorAnnotation(() => new ChiselStageSpec.Foo)))

info(s"'$expectedSV' exists")
expectedSV should (exist)
info(s"'$expectedFir' exists")
expectedFir should (exist)

}

it("should support custom firtool options") {
val targetDir = new File("test_run_dir/ChiselStageSpec")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ ProblemFilters.exclude[ReversedMissingMethodProblem]("chisel3.experimental.Sourc
ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.internal.ErrorEntry.serialize")
# Constructor is package private
ProblemFilters.exclude[IncompatibleMethTypeProblem]("chisel3.stage.ChiselOptions.this")
ProblemFilters.exclude[DirectMissingMethodProblem]("circt.stage.CIRCTOptions.this")

0 comments on commit 066fb47

Please sign in to comment.