summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/src/main/scala/chisel3/experimental/Analog.scala2
-rw-r--r--core/src/main/scala/chisel3/internal/Error.scala77
-rw-r--r--src/main/scala/chisel3/stage/ChiselAnnotations.scala6
-rw-r--r--src/main/scala/chisel3/stage/ChiselStage.scala22
-rw-r--r--src/main/scala/chisel3/stage/phases/Elaborate.scala15
-rw-r--r--src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala9
-rw-r--r--src/test/scala/chiselTests/OneHotMuxSpec.scala12
-rw-r--r--src/test/scala/chiselTests/stage/ChiselMainSpec.scala191
-rw-r--r--src/test/scala/chiselTests/stage/ChiselStageSpec.scala64
9 files changed, 301 insertions, 97 deletions
diff --git a/core/src/main/scala/chisel3/experimental/Analog.scala b/core/src/main/scala/chisel3/experimental/Analog.scala
index 2ce2c86d..6cca81f5 100644
--- a/core/src/main/scala/chisel3/experimental/Analog.scala
+++ b/core/src/main/scala/chisel3/experimental/Analog.scala
@@ -56,6 +56,7 @@ final class Analog private (private[chisel3] val width: Width) extends Element {
case ChildBinding(parent) => parent.topBinding
// See https://github.com/freechipsproject/chisel3/pull/946
case SampleElementBinding(parent) => parent.topBinding
+ case a: MemTypeBinding[_] => a
}
targetTopBinding match {
@@ -83,4 +84,3 @@ final class Analog private (private[chisel3] val width: Width) extends Element {
object Analog {
def apply(width: Width): Analog = new Analog(width)
}
-
diff --git a/core/src/main/scala/chisel3/internal/Error.scala b/core/src/main/scala/chisel3/internal/Error.scala
index 60bb6e2b..134f4c87 100644
--- a/core/src/main/scala/chisel3/internal/Error.scala
+++ b/core/src/main/scala/chisel3/internal/Error.scala
@@ -5,12 +5,82 @@ package chisel3.internal
import scala.annotation.tailrec
import scala.collection.mutable.{ArrayBuffer, LinkedHashMap}
-class ChiselException(message: String, cause: Throwable = null) extends Exception(message, cause) {
+object ExceptionHelpers {
+
+ /** Root packages that are not typically relevant to Chisel user code. */
+ final val packageTrimlist: Set[String] = Set("chisel3", "scala", "java", "jdk", "sun", "sbt")
+
+ /** The object name of Chisel's internal `Builder`. */
+ final val builderName: String = chisel3.internal.Builder.getClass.getName
+
+ /** Return a stack trace element that looks like `... (someMessage)`.
+ * @param message an optional message to include
+ */
+ def ellipsis(message: Option[String] = None): StackTraceElement =
+ new StackTraceElement("..", " ", message.getOrElse(""), -1)
+
+ /** Utility methods that can be added to exceptions.
+ */
+ implicit class ThrowableHelpers(throwable: Throwable) {
+
+ /** For an exception, mutably trim a stack trace to user code only.
+ *
+ * This does the following actions to the stack trace:
+ *
+ * 1. From the top, remove elements while the (root) package matches the packageTrimlist
+ * 2. Optionally, from the bottom, remove elements until the class matches an anchor
+ * 3. From the anchor (or the bottom), remove elements while the (root) package matches the packageTrimlist
+ *
+ * @param packageTrimlist packages that should be removed from the stack trace
+ * @param anchor an optional class name at which user execution might begin, e.g., a main object
+ * @return nothing as this mutates the exception directly
+ */
+ def trimStackTraceToUserCode(
+ packageTrimlist: Set[String] = packageTrimlist,
+ anchor: Option[String] = Some(builderName)
+ ): Unit = {
+ def inTrimlist(ste: StackTraceElement) = {
+ val packageName = ste.getClassName().takeWhile(_ != '.')
+ packageTrimlist.contains(packageName)
+ }
+
+ // Step 1: Remove elements from the top in the package trimlist
+ ((a: Array[StackTraceElement]) => a.view.dropWhile(inTrimlist))
+ // Step 2: Optionally remove elements from the bottom until the anchor
+ .andThen(_.reverse)
+ .andThen( a =>
+ anchor match {
+ case Some(b) => a.dropWhile(ste => !ste.getClassName.startsWith(b))
+ case None => a
+ }
+ )
+ // Step 3: Remove elements from the bottom in the package trimlist
+ .andThen(_.dropWhile(inTrimlist))
+ // Step 4: Reverse back to the original order
+ .andThen(_.reverse.toArray)
+ // Step 5: Add ellipsis stack trace elements and "--full-stacktrace" info
+ .andThen(a =>
+ ellipsis() +:
+ a :+
+ ellipsis() :+
+ ellipsis(Some("Stack trace trimmed to user code only. Rerun with --full-stacktrace to see the full stack trace")))
+ // Step 5: Mutate the stack trace in this exception
+ .andThen(throwable.setStackTrace(_))
+ .apply(throwable.getStackTrace)
+ }
+
+ }
+
+}
+
+class ChiselException(message: String, cause: Throwable = null) extends Exception(message, cause, true, true) {
/** Package names whose stack trace elements should be trimmed when generating a trimmed stack trace */
+ @deprecated("Use ExceptionHelpers.packageTrimlist. This will be removed in Chisel 3.6", "3.5")
val blacklistPackages: Set[String] = Set("chisel3", "scala", "java", "sun", "sbt")
/** The object name of Chisel's internal `Builder`. Everything stack trace element after this will be trimmed. */
+ @deprecated("Use ExceptionHelpers.builderName. This will be removed in Chisel 3.6", "3.5")
val builderName: String = chisel3.internal.Builder.getClass.getName
/** Examine a [[Throwable]], to extract all its causes. Innermost cause is first.
@@ -27,7 +97,7 @@ class ChiselException(message: String, cause: Throwable = null) extends Exceptio
/** Returns true if an exception contains */
private def containsBuilder(throwable: Throwable): Boolean =
throwable.getStackTrace().collectFirst {
- case ste if ste.getClassName().startsWith(builderName) => throwable
+ case ste if ste.getClassName().startsWith(ExceptionHelpers.builderName) => throwable
}.isDefined
/** Examine this [[ChiselException]] and it's causes for the first [[Throwable]] that contains a stack trace including
@@ -142,7 +212,8 @@ private[chisel3] class ErrorLog {
}
if (!allErrors.isEmpty) {
- throwException("Fatal errors during hardware elaboration")
+ throw new ChiselException("Fatal errors during hardware elaboration. Look above for error list.")
+ with scala.util.control.NoStackTrace
} else {
// No fatal errors, clear accumulated warnings since they've been reported
errors.clear()
diff --git a/src/main/scala/chisel3/stage/ChiselAnnotations.scala b/src/main/scala/chisel3/stage/ChiselAnnotations.scala
index bbe86ab4..04246fc5 100644
--- a/src/main/scala/chisel3/stage/ChiselAnnotations.scala
+++ b/src/main/scala/chisel3/stage/ChiselAnnotations.scala
@@ -56,13 +56,9 @@ case class ChiselGeneratorAnnotation(gen: () => RawModule) extends NoTargetAnnot
/** Run elaboration on the Chisel module generator function stored by this [[firrtl.annotations.Annotation]]
*/
- def elaborate: AnnotationSeq = try {
+ def elaborate: AnnotationSeq = {
val (circuit, dut) = Builder.build(Module(gen()))
Seq(ChiselCircuitAnnotation(circuit), DesignAnnotation(dut))
- } catch {
- case e @ (_: OptionsException | _: ChiselException) => throw e
- case e: Throwable =>
- throw new ChiselException(s"Exception thrown when elaborating ChiselGeneratorAnnotation", e)
}
}
diff --git a/src/main/scala/chisel3/stage/ChiselStage.scala b/src/main/scala/chisel3/stage/ChiselStage.scala
index aae7ad8d..1bcf5124 100644
--- a/src/main/scala/chisel3/stage/ChiselStage.scala
+++ b/src/main/scala/chisel3/stage/ChiselStage.scala
@@ -13,7 +13,7 @@ import firrtl.{
VerilogEmitter,
SystemVerilogEmitter
}
-import firrtl.options.{Dependency, Phase, PhaseManager, Shell, Stage, StageError, StageMain}
+import firrtl.options.{Dependency, Phase, PhaseManager, Shell, Stage, StageMain}
import firrtl.options.phases.DeletedWrapper
import firrtl.stage.{FirrtlCircuitAnnotation, FirrtlCli, RunFirrtlTransformAnnotation}
import firrtl.options.Viewer.view
@@ -28,7 +28,7 @@ class ChiselStage extends Stage {
override def prerequisites = Seq.empty
override def optionalPrerequisites = Seq.empty
- override def optionalPrerequisiteOf = Seq.empty
+ override def optionalPrerequisiteOf = Seq(Dependency[firrtl.stage.FirrtlStage])
override def invalidates(a: Phase) = false
val shell: Shell = new Shell("chisel") with ChiselCli with FirrtlCli
@@ -42,23 +42,7 @@ class ChiselStage extends Stage {
}
}
- def run(annotations: AnnotationSeq): AnnotationSeq = try {
- phaseManager.transform(annotations)
- } catch {
- case ce: ChiselException =>
- val stackTrace = if (!view[ChiselOptions](annotations).printFullStackTrace) {
- ce.chiselStackTrace
- } else {
- val sw = new StringWriter
- ce.printStackTrace(new PrintWriter(sw))
- sw.toString
- }
- Predef
- .augmentString(stackTrace)
- .lines
- .foreach(line => println(s"${ErrorLog.errTag} $line"))
- throw new StageError(cause=ce)
- }
+ def run(annotations: AnnotationSeq): AnnotationSeq = phaseManager.transform(annotations)
/** Convert a Chisel module to a CHIRRTL string
* @param gen a call-by-name Chisel module
diff --git a/src/main/scala/chisel3/stage/phases/Elaborate.scala b/src/main/scala/chisel3/stage/phases/Elaborate.scala
index 04cfc33e..df39b66b 100644
--- a/src/main/scala/chisel3/stage/phases/Elaborate.scala
+++ b/src/main/scala/chisel3/stage/phases/Elaborate.scala
@@ -6,6 +6,7 @@ import java.io.{PrintWriter, StringWriter}
import chisel3.ChiselException
import chisel3.internal.ErrorLog
+import chisel3.internal.ExceptionHelpers.ThrowableHelpers
import chisel3.stage.{ChiselGeneratorAnnotation, ChiselOptions}
import firrtl.AnnotationSeq
import firrtl.options.Viewer.view
@@ -21,8 +22,18 @@ class Elaborate extends Phase {
override def invalidates(a: Phase) = false
def transform(annotations: AnnotationSeq): AnnotationSeq = annotations.flatMap {
- case a: ChiselGeneratorAnnotation => a.elaborate
- case a => Some(a)
+ case a: ChiselGeneratorAnnotation => try {
+ a.elaborate
+ } catch {
+ /* if any throwable comes back and we're in "stack trace trimming" mode, then print an error and trim the stack trace
+ */
+ case scala.util.control.NonFatal(a) =>
+ if (!view[ChiselOptions](annotations).printFullStackTrace) {
+ a.trimStackTraceToUserCode()
+ }
+ throw(a)
+ }
+ case a => Some(a)
}
}
diff --git a/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala b/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala
index 6d3d526c..40358d11 100644
--- a/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala
+++ b/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala
@@ -8,16 +8,15 @@ import org.scalatest.exceptions.TestFailedException
class ChiselTestUtilitiesSpec extends ChiselFlatSpec {
// Who tests the testers?
"assertKnownWidth" should "error when the expected width is wrong" in {
- val caught = intercept[ChiselException] {
+ intercept[TestFailedException] {
assertKnownWidth(7) {
Wire(UInt(8.W))
}
}
- assert(caught.getCause.isInstanceOf[TestFailedException])
}
it should "error when the width is unknown" in {
- a [ChiselException] shouldBe thrownBy {
+ intercept[ChiselException] {
assertKnownWidth(7) {
Wire(UInt())
}
@@ -31,12 +30,11 @@ class ChiselTestUtilitiesSpec extends ChiselFlatSpec {
}
"assertInferredWidth" should "error if the width is known" in {
- val caught = intercept[ChiselException] {
+ intercept[TestFailedException] {
assertInferredWidth(8) {
Wire(UInt(8.W))
}
}
- assert(caught.getCause.isInstanceOf[TestFailedException])
}
it should "error if the expected width is wrong" in {
@@ -57,4 +55,3 @@ class ChiselTestUtilitiesSpec extends ChiselFlatSpec {
}
}
}
-
diff --git a/src/test/scala/chiselTests/OneHotMuxSpec.scala b/src/test/scala/chiselTests/OneHotMuxSpec.scala
index 887843d4..7608a3e7 100644
--- a/src/test/scala/chiselTests/OneHotMuxSpec.scala
+++ b/src/test/scala/chiselTests/OneHotMuxSpec.scala
@@ -37,26 +37,18 @@ class OneHotMuxSpec extends AnyFreeSpec with Matchers with ChiselRunners {
}
}
"simple one hot mux with all fixed width bundles but with different bundles should Not work" in {
- try {
+ intercept[IllegalArgumentException] {
assertTesterPasses(new DifferentBundleOneHotTester)
- } catch {
- case a: ChiselException => a.getCause match {
- case _: IllegalArgumentException =>
- }
}
}
"UIntToOH with output width greater than 2^(input width)" in {
assertTesterPasses(new UIntToOHTester)
}
"UIntToOH should not accept width of zero (until zero-width wires are fixed" in {
- try {
+ intercept[IllegalArgumentException] {
assertTesterPasses(new BasicTester {
val out = UIntToOH(0.U, 0)
})
- } catch {
- case a: ChiselException => a.getCause match {
- case _: IllegalArgumentException =>
- }
}
}
diff --git a/src/test/scala/chiselTests/stage/ChiselMainSpec.scala b/src/test/scala/chiselTests/stage/ChiselMainSpec.scala
index 0fc42fc6..1634e765 100644
--- a/src/test/scala/chiselTests/stage/ChiselMainSpec.scala
+++ b/src/test/scala/chiselTests/stage/ChiselMainSpec.scala
@@ -10,6 +10,8 @@ import chisel3.aop.inspecting.{InspectingAspect, InspectorAspect}
import org.scalatest.GivenWhenThen
import org.scalatest.featurespec.AnyFeatureSpec
import org.scalatest.matchers.should.Matchers
+import org.scalatest.Inside._
+import org.scalatest.EitherValues._
import scala.io.Source
import firrtl.Parser
@@ -32,7 +34,7 @@ object ChiselMainSpec {
/** A module that fails a requirement */
class FailingRequirementModule extends RawModule {
- require(false)
+ require(false, "the user wrote a failing requirement")
}
/** A module that triggers a Builder.error (as opposed to exception) */
@@ -69,14 +71,35 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit
args: Array[String],
generator: Option[Class[_ <: RawModule]] = None,
files: Seq[String] = Seq.empty,
- stdout: Option[String] = None,
- stderr: Option[String] = None,
+ stdout: Seq[Either[String, String]] = Seq.empty,
+ stderr: Seq[Either[String, String]] = Seq.empty,
result: Int = 0,
fileChecks: Map[String, File => Unit] = Map.empty) {
def testName: String = "args" + args.mkString("_")
def argsString: String = args.mkString(" ")
}
+ /** A test of ChiselMain that is going to involve catching an exception.
+ * @param args command line arguments (excluding --module) to pass in
+ * @param generator the module to build (used to generate --module)
+ * @param message snippets of text that should appear (Right) or not appear (Left) in the exception message
+ * @param stdout snippets of text that should appear (Right) or not appear (Left) in STDOUT
+ * @param stderr snippets of text that should appear (Right) or not appear (Left) in STDERR
+ * @param stackTrace snippets of text that should appear (Right) or not appear (Left) in the stack trace
+ * @tparam the type of exception that should occur
+ */
+ case class ChiselMainExceptionTest[A <: Throwable](
+ args: Array[String],
+ generator: Option[Class[_ <: RawModule]] = None,
+ message: Seq[Either[String, String]] = Seq.empty,
+ stdout: Seq[Either[String, String]] = Seq.empty,
+ stderr: Seq[Either[String, String]] = Seq.empty,
+ stackTrace: Seq[Either[String, String]] = Seq.empty
+ ) {
+ def testName: String = "args" + args.mkString("_")
+ def argsString: String = args.mkString(" ")
+ }
+
def runStageExpectFiles(p: ChiselMainTest): Unit = {
Scenario(s"""User runs Chisel Stage with '${p.argsString}'""") {
val f = new ChiselMainFixture
@@ -85,32 +108,33 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit
p.files.foreach( f => new File(td.buildDir + s"/$f").delete() )
When(s"""the user tries to compile with '${p.argsString}'""")
+ val module: Array[String] =
+ (if (p.generator.nonEmpty) { Array("--module", p.generator.get.getName) }
+ else { Array.empty[String] })
+ f.stage.main(Array("-td", td.buildDir.toString) ++ module ++ p.args)
val (stdout, stderr, result) =
grabStdOutErr {
catchStatus {
- val module: Array[String] = Array("foo") ++
- (if (p.generator.nonEmpty) { Array("--module", p.generator.get.getName) }
- else { Array.empty[String] })
f.stage.main(Array("-td", td.buildDir.toString) ++ module ++ p.args)
}
}
- p.stdout match {
- case Some(a) =>
+ p.stdout.foreach {
+ case Right(a) =>
Then(s"""STDOUT should include "$a"""")
stdout should include (a)
- case None =>
- Then(s"nothing should print to STDOUT")
- stdout should be (empty)
+ case Left(a) =>
+ Then(s"""STDOUT should not include "$a"""")
+ stdout should not include (a)
}
- p.stderr match {
- case Some(a) =>
- And(s"""STDERR should include "$a"""")
+ p.stderr.foreach {
+ case Right(a) =>
+ Then(s"""STDERR should include "$a"""")
stderr should include (a)
- case None =>
- And(s"nothing should print to STDERR")
- stderr should be (empty)
+ case Left(a) =>
+ Then(s"""STDERR should not include "$a"""")
+ stderr should not include (a)
}
p.result match {
@@ -131,56 +155,128 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit
}
}
+ /** Run a ChiselMainExceptionTest and verify that all the properties it spells out hold.
+ * @param p the test to run
+ * @tparam the type of the exception to catch (you shouldn't have to explicitly provide this)
+ */
+ def runStageExpectException[A <: Throwable: scala.reflect.ClassTag](p: ChiselMainExceptionTest[A]): Unit = {
+ Scenario(s"""User runs Chisel Stage with '${p.argsString}'""") {
+ val f = new ChiselMainFixture
+ val td = new TargetDirectoryFixture(p.testName)
+
+ When(s"""the user tries to compile with '${p.argsString}'""")
+ val module: Array[String] =
+ (if (p.generator.nonEmpty) { Array("--module", p.generator.get.getName) }
+ else { Array.empty[String] })
+ val (stdout, stderr, result) =
+ grabStdOutErr {
+ catchStatus {
+ intercept[A] {
+ f.stage.main(Array("-td", td.buildDir.toString) ++ module ++ p.args)
+ }
+ }
+ }
+
+ Then("the expected exception was thrown")
+ result should be a ('right)
+ val exception = result.right.get
+ info(s""" - Exception was a "${exception.getClass.getName}"""")
+
+ val message = exception.getMessage
+ p.message.foreach {
+ case Right(a) =>
+ Then(s"""STDOUT should include "$a"""")
+ message should include (a)
+ case Left(a) =>
+ Then(s"""STDOUT should not include "$a"""")
+ message should not include (a)
+ }
+
+ p.stdout.foreach {
+ case Right(a) =>
+ Then(s"""STDOUT should include "$a"""")
+ stdout should include (a)
+ case Left(a) =>
+ Then(s"""STDOUT should not include "$a"""")
+ stdout should not include (a)
+ }
+
+ p.stderr.foreach {
+ case Right(a) =>
+ Then(s"""STDERR should include "$a"""")
+ stderr should include (a)
+ case Left(a) =>
+ Then(s"""STDERR should not include "$a"""")
+ stderr should not include (a)
+ }
+
+ val stackTraceString = exception.getStackTrace.mkString("\n")
+ p.stackTrace.foreach {
+ case Left(a) =>
+ And(s"""the stack does not include "$a"""")
+ stackTraceString should not include (a)
+ case Right(a) =>
+ And(s"""the stack trace includes "$a"""")
+ stackTraceString should include (a)
+ }
+
+ }
+ }
+
info("As a Chisel user")
info("I compile a design")
Feature("show elaborating message") {
runStageExpectFiles(
ChiselMainTest(args = Array("-X", "high"),
- generator = Some(classOf[SameTypesModule]),
- stdout = Some("Done elaborating.")
+ generator = Some(classOf[SameTypesModule])
)
)
}
info("I screw up and compile some bad code")
- Feature("Stack trace trimming") {
+ Feature("Stack trace trimming of ChiselException") {
Seq(
- ChiselMainTest(args = Array("-X", "low"),
- generator = Some(classOf[DifferentTypesModule]),
- stdout = Some("Stack trace trimmed to user code only"),
- result = 1),
- ChiselMainTest(args = Array("-X", "high", "--full-stacktrace"),
- generator = Some(classOf[DifferentTypesModule]),
- stdout = Some("org.scalatest"),
- result = 1)
- ).foreach(runStageExpectFiles)
+ ChiselMainExceptionTest[chisel3.internal.ChiselException](
+ args = Array("-X", "low"),
+ generator = Some(classOf[DifferentTypesModule]),
+ stackTrace = Seq(Left("java"), Right(classOf[DifferentTypesModule].getName))
+ ),
+ ChiselMainExceptionTest[chisel3.internal.ChiselException](
+ args = Array("-X", "low", "--full-stacktrace"),
+ generator = Some(classOf[DifferentTypesModule]),
+ stackTrace = Seq(Right("java"), Right(classOf[DifferentTypesModule].getName))
+ )
+ ).foreach(runStageExpectException)
}
- Feature("Report properly trimmed stack traces") {
+ Feature("Stack trace trimming of user exceptions") {
Seq(
- ChiselMainTest(args = Array("-X", "low"),
- generator = Some(classOf[FailingRequirementModule]),
- stdout = Some("requirement failed"),
- result = 1),
- ChiselMainTest(args = Array("-X", "low", "--full-stacktrace"),
- generator = Some(classOf[FailingRequirementModule]),
- stdout = Some("chisel3.internal.ChiselException"),
- result = 1)
- ).foreach(runStageExpectFiles)
+ ChiselMainExceptionTest[java.lang.IllegalArgumentException](
+ args = Array("-X", "low"),
+ generator = Some(classOf[FailingRequirementModule]),
+ stackTrace = Seq(Right(classOf[FailingRequirementModule].getName), Left("java"))
+ ),
+ ChiselMainExceptionTest[java.lang.IllegalArgumentException](
+ args = Array("-X", "low", "--full-stacktrace"),
+ generator = Some(classOf[FailingRequirementModule]),
+ stackTrace = Seq(Right(classOf[FailingRequirementModule].getName), Right("java"))
+ )
+ ).foreach(runStageExpectException)
}
- Feature("Builder.error source locator") {
+ Feature("Stack trace trimming and Builder.error errors") {
Seq(
- ChiselMainTest(args = Array("-X", "none"),
+ ChiselMainExceptionTest[chisel3.internal.ChiselException](
+ args = Array("-X", "low"),
generator = Some(classOf[BuilderErrorModule]),
- stdout = Some("ChiselMainSpec.scala:41: Invalid bit range (3,-1) in class chiselTests.stage.ChiselMainSpec$BuilderErrorModule"),
- result = 1)
- ).foreach(runStageExpectFiles)
+ message = Seq(Right("Fatal errors during hardware elaboration")),
+ stdout = Seq(Right("ChiselMainSpec.scala:43: Invalid bit range (3,-1) in class chiselTests.stage.ChiselMainSpec$BuilderErrorModule"))
+ )
+ ).foreach(runStageExpectException)
}
Feature("Specifying a custom output file") {
runStageExpectFiles(ChiselMainTest(
args = Array("--chisel-output-file", "Foo", "--no-run-firrtl"),
generator = Some(classOf[SameTypesModule]),
- stdout = Some(""),
files = Seq("Foo.fir"),
fileChecks = Map(
"Foo.fir" -> { file =>
@@ -192,7 +288,6 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit
runStageExpectFiles(ChiselMainTest(
args = Array("--chisel-output-file", "Foo.pb", "--no-run-firrtl"),
generator = Some(classOf[SameTypesModule]),
- stdout = Some(""),
files = Seq("Foo.pb"),
fileChecks = Map(
"Foo.pb" -> { file =>
@@ -209,10 +304,10 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit
Seq(
ChiselMainTest(args = Array( "-X", "high", "--with-aspect", "chiselTests.stage.TestClassAspect" ),
generator = Some(classOf[SameTypesModule]),
- stdout = Some("Ran inspectingAspect")),
+ stdout = Seq(Right("Ran inspectingAspect"))),
ChiselMainTest(args = Array( "-X", "high", "--with-aspect", "chiselTests.stage.TestObjectAspect" ),
generator = Some(classOf[SameTypesModule]),
- stdout = Some("Ran inspectingAspect"))
+ stdout = Seq(Right("Ran inspectingAspect")))
).foreach(runStageExpectFiles)
}
diff --git a/src/test/scala/chiselTests/stage/ChiselStageSpec.scala b/src/test/scala/chiselTests/stage/ChiselStageSpec.scala
index 98bbb2ea..167e414b 100644
--- a/src/test/scala/chiselTests/stage/ChiselStageSpec.scala
+++ b/src/test/scala/chiselTests/stage/ChiselStageSpec.scala
@@ -30,6 +30,10 @@ object ChiselStageSpec {
out := memory(bar.out)
}
+ class UserExceptionModule extends RawModule {
+ assert(false, "User threw an exception")
+ }
+
}
class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils {
@@ -40,13 +44,13 @@ class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils {
val stage = new ChiselStage
}
- behavior of "ChiselStage.emitChirrtl"
+ behavior of "ChiselStage$.emitChirrtl"
it should "return a CHIRRTL string" in {
ChiselStage.emitChirrtl(new Foo) should include ("infer mport")
}
- behavior of "ChiselStage.emitFirrtl"
+ behavior of "ChiselStage$.emitFirrtl"
it should "return a High FIRRTL string" in {
ChiselStage.emitFirrtl(new Foo) should include ("mem memory")
@@ -58,7 +62,7 @@ class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils {
.emitFirrtl(new Foo, args) should include ("module Bar")
}
- behavior of "ChiselStage.emitVerilog"
+ behavior of "ChiselStage$.emitVerilog"
it should "return a Verilog string" in {
ChiselStage.emitVerilog(new Foo) should include ("endmodule")
@@ -142,4 +146,58 @@ class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils {
exactly (1, order) should be (Dependency[chisel3.stage.phases.Elaborate])
}
+ behavior of "ChiselStage$ exception handling"
+
+ it should "truncate a user exception" in {
+ info("The user's java.lang.AssertionError was thrown")
+ val exception = intercept[java.lang.AssertionError] {
+ ChiselStage.emitChirrtl(new UserExceptionModule)
+ }
+
+ val message = exception.getMessage
+ info("The exception includes the user's message")
+ message should include ("User threw an exception")
+
+ info("The stack trace is trimmed")
+ exception.getStackTrace.mkString("\n") should not include ("java")
+ }
+
+ behavior of "ChiselStage exception handling"
+
+ it should "truncate a user exception" in {
+ info("The user's java.lang.AssertionError was thrown")
+ val exception = intercept[java.lang.AssertionError] {
+ (new ChiselStage).emitChirrtl(new UserExceptionModule)
+ }
+
+ info(s""" - Exception was a ${exception.getClass.getName}""")
+
+ val message = exception.getMessage
+ info("The exception includes the user's message")
+ message should include ("User threw an exception")
+
+ val stackTrace = exception.getStackTrace.mkString("\n")
+ info("The stack trace is trimmed")
+ stackTrace should not include ("java")
+
+ info("The stack trace include information about running --full-stacktrace")
+ stackTrace should include ("--full-stacktrace")
+ }
+
+ it should """not truncate a user exception with "--full-stacktrace"""" in {
+ info("The user's java.lang.AssertionError was thrown")
+ val exception = intercept[java.lang.AssertionError] {
+ (new ChiselStage).emitChirrtl(new UserExceptionModule, Array("--full-stacktrace"))
+ }
+
+ info(s""" - Exception was a ${exception.getClass.getName}""")
+
+ val message = exception.getMessage
+ info("The exception includes the user's message")
+ message should include ("User threw an exception")
+
+ info("The stack trace is not trimmed")
+ exception.getStackTrace.mkString("\n") should include ("java")
+ }
+
}