summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/scala/chisel3/aop/injecting/InjectingAspect.scala6
-rw-r--r--src/main/scala/chisel3/stage/ChiselAnnotations.scala18
-rw-r--r--src/main/scala/chisel3/stage/ChiselCli.scala1
-rw-r--r--src/main/scala/chisel3/stage/ChiselOptions.scala3
-rw-r--r--src/main/scala/chisel3/stage/package.scala5
-rw-r--r--src/main/scala/chisel3/stage/phases/Elaborate.scala14
-rw-r--r--src/main/scala/chisel3/util/Bitwise.scala2
-rw-r--r--src/main/scala/chisel3/util/GrayCode.scala24
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala11
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala49
-rw-r--r--src/test/scala/chiselTests/BundleElementsSpec.scala564
-rw-r--r--src/test/scala/chiselTests/BundleSpec.scala10
-rw-r--r--src/test/scala/chiselTests/CompatibilitySpec.scala23
-rw-r--r--src/test/scala/chiselTests/MultiClockSpec.scala110
-rw-r--r--src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala22
-rw-r--r--src/test/scala/chiselTests/stage/ChiselStageSpec.scala83
16 files changed, 912 insertions, 33 deletions
diff --git a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala
index abe208cf..ba873c23 100644
--- a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala
+++ b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala
@@ -6,9 +6,10 @@ import chisel3.{withClockAndReset, Module, ModuleAspect, RawModule}
import chisel3.aop._
import chisel3.internal.{Builder, DynamicContext}
import chisel3.internal.firrtl.DefModule
-import chisel3.stage.DesignAnnotation
+import chisel3.stage.{ChiselOptions, DesignAnnotation}
import firrtl.annotations.ModuleTarget
import firrtl.stage.RunFirrtlTransformAnnotation
+import firrtl.options.Viewer.view
import firrtl.{ir, _}
import scala.collection.mutable
@@ -56,7 +57,8 @@ abstract class InjectorAspect[T <: RawModule, M <: RawModule](
*/
final def toAnnotation(modules: Iterable[M], circuit: String, moduleNames: Seq[String]): AnnotationSeq = {
RunFirrtlTransformAnnotation(new InjectingTransform) +: modules.map { module =>
- val dynamicContext = new DynamicContext(annotationsInAspect)
+ val chiselOptions = view[ChiselOptions](annotationsInAspect)
+ val dynamicContext = new DynamicContext(annotationsInAspect, chiselOptions.throwOnFirstError)
// Add existing module names into the namespace. If injection logic instantiates new modules
// which would share the same name, they will get uniquified accordingly
moduleNames.foreach { n =>
diff --git a/src/main/scala/chisel3/stage/ChiselAnnotations.scala b/src/main/scala/chisel3/stage/ChiselAnnotations.scala
index c5811813..e046319d 100644
--- a/src/main/scala/chisel3/stage/ChiselAnnotations.scala
+++ b/src/main/scala/chisel3/stage/ChiselAnnotations.scala
@@ -61,6 +61,24 @@ case object PrintFullStackTraceAnnotation
}
+/** On recoverable errors, this will cause Chisel to throw an exception instead of continuing.
+ */
+case object ThrowOnFirstErrorAnnotation
+ extends NoTargetAnnotation
+ with ChiselOption
+ with HasShellOptions
+ with Unserializable {
+
+ val options = Seq(
+ new ShellOption[Unit](
+ longOption = "throw-on-first-error",
+ toAnnotationSeq = _ => Seq(ThrowOnFirstErrorAnnotation),
+ helpText = "Throw an exception on the first error instead of continuing"
+ )
+ )
+
+}
+
/** An [[firrtl.annotations.Annotation]] storing a function that returns a Chisel module
* @param gen a generator function
*/
diff --git a/src/main/scala/chisel3/stage/ChiselCli.scala b/src/main/scala/chisel3/stage/ChiselCli.scala
index 26b032bf..f38bf50c 100644
--- a/src/main/scala/chisel3/stage/ChiselCli.scala
+++ b/src/main/scala/chisel3/stage/ChiselCli.scala
@@ -9,6 +9,7 @@ trait ChiselCli { this: Shell =>
Seq(
NoRunFirrtlCompilerAnnotation,
PrintFullStackTraceAnnotation,
+ ThrowOnFirstErrorAnnotation,
ChiselOutputFileAnnotation,
ChiselGeneratorAnnotation
)
diff --git a/src/main/scala/chisel3/stage/ChiselOptions.scala b/src/main/scala/chisel3/stage/ChiselOptions.scala
index ed4d0a2f..7e4305fa 100644
--- a/src/main/scala/chisel3/stage/ChiselOptions.scala
+++ b/src/main/scala/chisel3/stage/ChiselOptions.scala
@@ -7,12 +7,14 @@ import chisel3.internal.firrtl.Circuit
class ChiselOptions private[stage] (
val runFirrtlCompiler: Boolean = true,
val printFullStackTrace: Boolean = false,
+ val throwOnFirstError: Boolean = false,
val outputFile: Option[String] = None,
val chiselCircuit: Option[Circuit] = None) {
private[stage] def copy(
runFirrtlCompiler: Boolean = runFirrtlCompiler,
printFullStackTrace: Boolean = printFullStackTrace,
+ throwOnFirstError: Boolean = throwOnFirstError,
outputFile: Option[String] = outputFile,
chiselCircuit: Option[Circuit] = chiselCircuit
): ChiselOptions = {
@@ -20,6 +22,7 @@ class ChiselOptions private[stage] (
new ChiselOptions(
runFirrtlCompiler = runFirrtlCompiler,
printFullStackTrace = printFullStackTrace,
+ throwOnFirstError = throwOnFirstError,
outputFile = outputFile,
chiselCircuit = chiselCircuit
)
diff --git a/src/main/scala/chisel3/stage/package.scala b/src/main/scala/chisel3/stage/package.scala
index bf03e2df..b1064c05 100644
--- a/src/main/scala/chisel3/stage/package.scala
+++ b/src/main/scala/chisel3/stage/package.scala
@@ -15,8 +15,9 @@ package object stage {
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 NoRunFirrtlCompilerAnnotation => c.copy(runFirrtlCompiler = false)
+ case PrintFullStackTraceAnnotation => c.copy(printFullStackTrace = true)
+ case ThrowOnFirstErrorAnnotation => c.copy(throwOnFirstError = true)
case ChiselOutputFileAnnotation(f) => c.copy(outputFile = Some(f))
case ChiselCircuitAnnotation(a) => c.copy(chiselCircuit = Some(a))
}
diff --git a/src/main/scala/chisel3/stage/phases/Elaborate.scala b/src/main/scala/chisel3/stage/phases/Elaborate.scala
index 2cfb3200..55331cb4 100644
--- a/src/main/scala/chisel3/stage/phases/Elaborate.scala
+++ b/src/main/scala/chisel3/stage/phases/Elaborate.scala
@@ -5,7 +5,13 @@ package chisel3.stage.phases
import chisel3.Module
import chisel3.internal.ExceptionHelpers.ThrowableHelpers
import chisel3.internal.{Builder, DynamicContext}
-import chisel3.stage.{ChiselCircuitAnnotation, ChiselGeneratorAnnotation, ChiselOptions, DesignAnnotation}
+import chisel3.stage.{
+ ChiselCircuitAnnotation,
+ ChiselGeneratorAnnotation,
+ ChiselOptions,
+ DesignAnnotation,
+ ThrowOnFirstErrorAnnotation
+}
import firrtl.AnnotationSeq
import firrtl.options.Phase
import firrtl.options.Viewer.view
@@ -21,14 +27,16 @@ class Elaborate extends Phase {
def transform(annotations: AnnotationSeq): AnnotationSeq = annotations.flatMap {
case ChiselGeneratorAnnotation(gen) =>
+ val chiselOptions = view[ChiselOptions](annotations)
try {
- val (circuit, dut) = Builder.build(Module(gen()), new DynamicContext(annotations))
+ val (circuit, dut) =
+ Builder.build(Module(gen()), new DynamicContext(annotations, chiselOptions.throwOnFirstError))
Seq(ChiselCircuitAnnotation(circuit), DesignAnnotation(dut))
} 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) {
+ if (!chiselOptions.printFullStackTrace) {
a.trimStackTraceToUserCode()
}
throw (a)
diff --git a/src/main/scala/chisel3/util/Bitwise.scala b/src/main/scala/chisel3/util/Bitwise.scala
index 0d8318bf..8abe3645 100644
--- a/src/main/scala/chisel3/util/Bitwise.scala
+++ b/src/main/scala/chisel3/util/Bitwise.scala
@@ -14,7 +14,7 @@ import chisel3._
* FillInterleaved(2, "b1 0 0 1".U) // equivalent to "b11 00 00 11".U
* FillInterleaved(2, myUIntWire) // dynamic interleaved fill
*
- * FillInterleaved(2, Seq(true.B, false.B, false.B, false.B)) // equivalent to "b11 00 00 00".U
+ * FillInterleaved(2, Seq(false.B, false.B, false.B, true.B)) // equivalent to "b11 00 00 00".U
* FillInterleaved(2, Seq(true.B, false.B, false.B, true.B)) // equivalent to "b11 00 00 11".U
* }}}
*/
diff --git a/src/main/scala/chisel3/util/GrayCode.scala b/src/main/scala/chisel3/util/GrayCode.scala
new file mode 100644
index 00000000..ef310ee9
--- /dev/null
+++ b/src/main/scala/chisel3/util/GrayCode.scala
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.util
+
+import chisel3._
+
+object BinaryToGray {
+
+ /** Turns a binary number into gray code. */
+ def apply(in: UInt): UInt = in ^ (in >> 1)
+}
+
+object GrayToBinary {
+
+ /** Inverts the [[BinaryToGray]] operation. */
+ def apply(in: UInt, width: Int): UInt = apply(in(width - 1, 0))
+
+ /** Inverts the [[BinaryToGray]] operation. */
+ def apply(in: UInt): UInt = if (in.getWidth < 2) { in }
+ else {
+ val bits = in.getWidth - 2 to 0 by -1
+ Cat(bits.scanLeft(in.head(1)) { case (prev, ii) => prev ^ in(ii) })
+ }
+}
diff --git a/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala
index de2f207b..86973e5b 100644
--- a/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala
+++ b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala
@@ -57,11 +57,20 @@ object EspressoMinimizer extends Minimizer with LazyLogging {
def readTable(espressoTable: String) = {
def bitPat(espresso: String): BitPat = BitPat("b" + espresso.replace('-', '?'))
- espressoTable
+ val out = espressoTable
.split("\n")
.filterNot(_.startsWith("."))
.map(_.split(' '))
.map(row => bitPat(row(0)) -> bitPat(row(1)))
+ // special case for 0 and DontCare, if output is not couple to input
+ if (out.isEmpty)
+ Array(
+ (
+ BitPat(s"b${"?" * table.inputWidth}"),
+ BitPat(s"b${"0" * table.outputWidth}")
+ )
+ )
+ else out
}
val input = writeTable(table)
diff --git a/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala
index a3481869..26a072f1 100644
--- a/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala
+++ b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala
@@ -304,24 +304,37 @@ object QMCMinimizer extends Minimizer {
)
})
- minimized.tail.foldLeft(table.copy(table = Seq(minimized.head))) {
- case (tb, t) =>
- if (tb.table.exists(x => x._1 == t._1)) {
- tb.copy(table = tb.table.map {
- case (k, v) =>
- if (k == t._1) {
- def ones(bitPat: BitPat) = bitPat.rawString.zipWithIndex.collect { case ('1', x) => x }
- (
- k,
- BitPat(
- "b" + (0 until v.getWidth).map(i => if ((ones(v) ++ ones(t._2)).contains(i)) "1" else "?").mkString
+ // special case for 0 and DontCare, if output is not couple to input
+ if (minimized.isEmpty)
+ table.copy(
+ Seq(
+ (
+ BitPat(s"b${"?" * table.inputWidth}"),
+ BitPat(s"b${"0" * table.outputWidth}")
+ )
+ )
+ )
+ else
+ minimized.tail.foldLeft(table.copy(table = Seq(minimized.head))) {
+ case (tb, t) =>
+ if (tb.table.exists(x => x._1 == t._1)) {
+ tb.copy(table = tb.table.map {
+ case (k, v) =>
+ if (k == t._1) {
+ def ones(bitPat: BitPat) = bitPat.rawString.zipWithIndex.collect { case ('1', x) => x }
+ (
+ k,
+ BitPat(
+ "b" + (0 until v.getWidth)
+ .map(i => if ((ones(v) ++ ones(t._2)).contains(i)) "1" else "?")
+ .mkString
+ )
)
- )
- } else (k, v)
- })
- } else {
- tb.copy(table = tb.table :+ t)
- }
- }
+ } else (k, v)
+ })
+ } else {
+ tb.copy(table = tb.table :+ t)
+ }
+ }
}
}
diff --git a/src/test/scala/chiselTests/BundleElementsSpec.scala b/src/test/scala/chiselTests/BundleElementsSpec.scala
new file mode 100644
index 00000000..fab2e733
--- /dev/null
+++ b/src/test/scala/chiselTests/BundleElementsSpec.scala
@@ -0,0 +1,564 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests
+
+import chisel3._
+import chisel3.experimental.{ChiselEnum, FixedPoint}
+import chisel3.stage.ChiselStage
+import chisel3.util.Decoupled
+import org.scalatest.exceptions.TestFailedException
+import org.scalatest.freespec.AnyFreeSpec
+import org.scalatest.matchers.should.Matchers
+
+import scala.language.reflectiveCalls
+
+/* Rich and complicated bundle examples
+ */
+trait BpipSuperTraitWithField {
+ val bpipSuperTraitGood = SInt(17.W)
+ def bpipSuperTraitBad = SInt(22.W)
+}
+
+trait BpipTraitWithField extends BpipSuperTraitWithField {
+ val bpipTraitGood = SInt(17.W)
+ def bpipTraitBad = SInt(22.W)
+}
+
+class BpipOneField extends Bundle with BpipTraitWithField {
+ val bpipOneFieldOne = SInt(8.W)
+ val bpipOneFieldTwo = SInt(8.W)
+}
+
+class BpipTwoField extends BpipOneField {
+ val bpipTwoFieldOne = SInt(8.W)
+ val bpipTwoFieldTwo = Vec(4, UInt(12.W))
+ val myInt = 7
+ val baz = Decoupled(UInt(16.W))
+}
+
+class BpipDecoupled extends BpipOneField {
+ val bpipDecoupledSInt = SInt(8.W)
+ val bpipDecoupledVec = Vec(4, UInt(12.W))
+ val bpipDecoupledDecoupled = Decoupled(UInt(16.W))
+}
+
+class HasDecoupledBundleByInheritance extends Module {
+ val out1 = IO(Output(new BpipDecoupled))
+ assertElementsMatchExpected(out1)(
+ "bpipDecoupledDecoupled" -> _.bpipDecoupledDecoupled,
+ "bpipDecoupledVec" -> _.bpipDecoupledVec,
+ "bpipDecoupledSInt" -> _.bpipDecoupledSInt,
+ "bpipOneFieldTwo" -> _.bpipOneFieldTwo,
+ "bpipOneFieldOne" -> _.bpipOneFieldOne,
+ "bpipTraitGood" -> _.bpipTraitGood,
+ "bpipSuperTraitGood" -> _.bpipSuperTraitGood
+ )
+}
+
+/* plugin should not affect the seq detection */
+class DebugProblem3 extends Module {
+ val out1 = IO(Output(new BpipTwoField))
+ assertElementsMatchExpected(out1)(
+ "baz" -> _.baz,
+ "bpipTwoFieldTwo" -> _.bpipTwoFieldTwo,
+ "bpipTwoFieldOne" -> _.bpipTwoFieldOne,
+ "bpipOneFieldTwo" -> _.bpipOneFieldTwo,
+ "bpipOneFieldOne" -> _.bpipOneFieldOne,
+ "bpipTraitGood" -> _.bpipTraitGood,
+ "bpipSuperTraitGood" -> _.bpipSuperTraitGood
+ )
+}
+
+class BpipBadSeqBundle extends Bundle {
+ val bpipBadSeqBundleGood = UInt(999.W)
+ val bpipBadSeqBundleBad = Seq(UInt(16.W), UInt(8.W), UInt(4.W))
+}
+
+class HasBadSeqBundle extends Module {
+ val out1 = IO(Output(new BpipBadSeqBundle))
+}
+
+class BpipBadSeqBundleWithIgnore extends Bundle with IgnoreSeqInBundle {
+ val goodFieldWithIgnore = UInt(999.W)
+ val badSeqFieldWithIgnore = Seq(UInt(16.W), UInt(8.W), UInt(4.W))
+}
+
+class UsesIgnoreSeqInBundle extends Module {
+ val out1 = IO(Output(new BpipBadSeqBundleWithIgnore))
+}
+
+/* This is mostly a test of the field order */
+class BpipP8_1 extends Bundle {
+ val field_1_1 = UInt(11.W)
+ val field_1_2 = UInt(12.W)
+}
+
+class BpipP8_2 extends BpipP8_1 {
+ val field_2_1 = UInt(11.W)
+ val field_2_2 = UInt(12.W)
+}
+
+class BpipP8_3 extends BpipP8_2 {
+ val field_3_1 = UInt(11.W)
+ val field_3_2 = UInt(12.W)
+}
+
+/* plugin should not affect the seq detection */
+class ForFieldOrderingTest extends Module {
+ val out1 = IO(Output(new BpipP8_3))
+ out1 := DontCare
+ assertElementsMatchExpected(out1)(
+ "field_3_2" -> _.field_3_2,
+ "field_3_1" -> _.field_3_1,
+ "field_2_2" -> _.field_2_2,
+ "field_2_1" -> _.field_2_1,
+ "field_1_2" -> _.field_1_2,
+ "field_1_1" -> _.field_1_1
+ )
+}
+
+/* plugin should allow parameter var fields */
+class HasValParamsToBundle extends Module {
+ // The following block does not work, suggesting that ParamIsField is not a case we need to solve
+ class BpipParamIsField0(val paramField0: UInt) extends Bundle
+ class BpipParamIsField1(val paramField1: UInt) extends BpipParamIsField0(UInt(66.W))
+
+ val out3 = IO(Output(new BpipParamIsField1(UInt(10.W))))
+ val out4 = IO(Output(new BpipParamIsField1(UInt(10.W))))
+ out3 := DontCare
+ assertElementsMatchExpected(out3)("paramField0" -> _.paramField0, "paramField1" -> _.paramField1)
+ assertElementsMatchExpected(out4)("paramField0" -> _.paramField0, "paramField1" -> _.paramField1)
+}
+
+class HasGenParamsPassedToSuperclasses extends Module {
+
+ class OtherBundle extends Bundle {
+ val otherField = UInt(55.W)
+ }
+
+ class BpipWithGen[T <: Data, TT <: Data](gen: T, gen2: => TT) extends Bundle {
+ val superFoo = gen
+ val superQux = gen2
+ }
+
+// class BpipDemoBundle[T <: Data](gen: T, gen2: => T) extends BpipTwoField with BpipVarmint {
+ class BpipUsesWithGen[T <: Data](gen: T, gen2: => T) extends BpipWithGen(gen, gen2) {
+ // val foo = gen
+ val bar = Bool()
+ val qux = gen2
+ val bad = 444
+ val baz = Decoupled(UInt(16.W))
+ }
+
+ val out1 = IO(Output(new BpipUsesWithGen(UInt(4.W), new OtherBundle)))
+ val out2 = IO(Output(new BpipUsesWithGen(UInt(4.W), FixedPoint(10.W, 4.BP))))
+
+ out1 := DontCare
+
+ assertElementsMatchExpected(out1)(
+ "baz" -> _.baz,
+ "qux" -> _.qux,
+ "bar" -> _.bar,
+ "superQux" -> _.superQux,
+ "superFoo" -> _.superFoo
+ )
+ assertElementsMatchExpected(out2)(
+ "baz" -> _.baz,
+ "qux" -> _.qux,
+ "bar" -> _.bar,
+ "superQux" -> _.superQux,
+ "superFoo" -> _.superFoo
+ )
+}
+
+/* Testing whether gen fields superFoo and superQux can be found when they are
+ * declared in a superclass
+ *
+ */
+class UsesGenFiedldsInSuperClass extends Module {
+ class BpipWithGen[T <: Data](gen: T) extends Bundle {
+ val superFoo = gen
+ val superQux = gen
+ }
+
+ class BpipUsesWithGen[T <: Data](gen: T) extends BpipWithGen(gen) {}
+
+ val out = IO(Output(new BpipUsesWithGen(UInt(4.W))))
+
+ out := DontCare
+
+ assertElementsMatchExpected(out)()
+}
+
+/* Testing whether gen fields superFoo and superQux can be found when they are
+ * declared in a superclass
+ *
+ */
+class BpipBadBundleWithHardware extends Bundle {
+ val bpipWithHardwareGood = UInt(8.W)
+ val bpipWithHardwareBad = 244.U(16.W)
+}
+
+class HasHardwareFieldsInBundle extends Module {
+ val out = IO(Output(new BpipBadBundleWithHardware))
+ assertElementsMatchExpected(out)()
+}
+
+/* This is legal because of =>
+ */
+class UsesBundleWithGeneratorField extends Module {
+ class BpipWithGen[T <: Data](gen: => T) extends Bundle {
+ val superFoo = gen
+ val superQux = gen
+ }
+
+ class BpipUsesWithGen[T <: Data](gen: => T) extends BpipWithGen(gen)
+
+ val out = IO(Output(new BpipUsesWithGen(UInt(4.W))))
+
+ out := DontCare
+
+ assertElementsMatchExpected(out)("superQux" -> _.superQux, "superFoo" -> _.superFoo)
+}
+
+/* Testing whether gen fields superFoo and superQux can be found when they are
+ * declared in a superclass
+ *
+ */
+class BundleElementsSpec extends AnyFreeSpec with Matchers {
+
+ /** Tests a whole bunch of different Bundle constructions
+ */
+ class BpipIsComplexBundle extends Module {
+
+ trait BpipVarmint {
+ val varmint = Bool()
+
+ def vermin = Bool()
+
+ private val puppy = Bool()
+ }
+
+ abstract class BpipAbstractBundle extends Bundle {
+ def doNothing: Unit
+
+ val fromAbstractBundle = UInt(22.W)
+ }
+
+ class BpipOneField extends Bundle {
+ val fieldOne = SInt(8.W)
+ }
+
+ class BpipTwoField extends BpipOneField {
+ val fieldTwo = SInt(8.W)
+ val fieldThree = Vec(4, UInt(12.W))
+ }
+
+ class BpipAnimalBundle(w1: Int, w2: Int) extends Bundle {
+ val dog = SInt(w1.W)
+ val fox = UInt(w2.W)
+ }
+
+ class BpipDemoBundle[T <: Data](gen: T, gen2: => T) extends BpipTwoField with BpipVarmint {
+ val foo = gen
+ val bar = Bool()
+ val qux = gen2
+ val bad = 44
+ val baz = Decoupled(UInt(16.W))
+ val animals = new BpipAnimalBundle(4, 8)
+ }
+
+ val out = IO(Output(new BpipDemoBundle(UInt(4.W), FixedPoint(10.W, 4.BP))))
+
+ val out2 = IO(Output(new BpipAbstractBundle {
+ override def doNothing: Unit = ()
+
+ val notAbstract: Bool = Bool()
+ }))
+
+ val out4 = IO(Output(new BpipAnimalBundle(99, 100)))
+ val out5 = IO(Output(new BpipTwoField))
+
+ out := DontCare
+ out5 := DontCare
+
+ assertElementsMatchExpected(out)(
+ "animals" -> _.animals,
+ "baz" -> _.baz,
+ "qux" -> _.qux,
+ "bar" -> _.bar,
+ "varmint" -> _.varmint,
+ "fieldThree" -> _.fieldThree,
+ "fieldTwo" -> _.fieldTwo,
+ "fieldOne" -> _.fieldOne,
+ "foo" -> _.foo
+ )
+ assertElementsMatchExpected(out5)("fieldThree" -> _.fieldThree, "fieldTwo" -> _.fieldTwo, "fieldOne" -> _.fieldOne)
+ assertElementsMatchExpected(out2)("notAbstract" -> _.notAbstract, "fromAbstractBundle" -> _.fromAbstractBundle)
+ assertElementsMatchExpected(out4)("fox" -> _.fox, "dog" -> _.dog)
+ }
+
+ "Complex Bundle with inheritance, traits and params. DebugProblem1" in {
+ ChiselStage.emitFirrtl(new BpipIsComplexBundle)
+ }
+
+ "Decoupled Bundle with inheritance" in {
+ ChiselStage.emitFirrtl(new HasDecoupledBundleByInheritance)
+ }
+
+ "Simple bundle inheritance. DebugProblem3" in {
+ ChiselStage.emitFirrtl(new DebugProblem3)
+ }
+
+ "Bundles containing Seq[Data] should be compile erorr. HasBadSeqBundle" in {
+ intercept[ChiselException] {
+ ChiselStage.emitFirrtl(new HasBadSeqBundle)
+ }
+ }
+
+ "IgnoreSeqInBundle allows Seq[Data] in bundle" in {
+ ChiselStage.emitFirrtl(new UsesIgnoreSeqInBundle)
+ }
+
+ "Simple field ordering test." in {
+ ChiselStage.emitFirrtl(new ForFieldOrderingTest)
+ }
+
+ "Val params to Bundle should be an Exception." in {
+ ChiselStage.emitFirrtl(new HasValParamsToBundle)
+ }
+
+ "Should handle gen params passed to superclasses" in {
+ ChiselStage.emitFirrtl(new HasGenParamsPassedToSuperclasses)
+ }
+
+ "Aliased fields should be detected and throw an exception, because gen: Data, with no =>" in {
+ intercept[AliasedAggregateFieldException] {
+ ChiselStage.emitFirrtl(new UsesGenFiedldsInSuperClass)
+ }
+ }
+
+ "Error when bundle fields are hardware, such as literal values. HasHardwareFieldsInBundle" in {
+ val e = intercept[ExpectedChiselTypeException] {
+ ChiselStage.emitFirrtl(new HasHardwareFieldsInBundle)
+ }
+ e.getMessage should include(
+ "Bundle: BpipBadBundleWithHardware contains hardware fields: bpipWithHardwareBad: UInt<16>(244)"
+ )
+ }
+
+ "Aliased fields not created when using gen: => Data" in {
+ ChiselStage.emitFirrtl(new UsesBundleWithGeneratorField)
+ }
+
+ class OptionBundle(val hasIn: Boolean) extends Bundle {
+ val in = if (hasIn) {
+ Some(Input(Bool()))
+ } else {
+ None
+ }
+ val out = Output(Bool())
+ }
+
+ class UsesBundleWithOptionFields extends Module {
+ val outTrue = IO(Output(new OptionBundle(hasIn = true)))
+ val outFalse = IO(Output(new OptionBundle(hasIn = false)))
+ //NOTE: The _.in.get _.in is an optional field
+ assertElementsMatchExpected(outTrue)("out" -> _.out, "in" -> _.in.get)
+ assertElementsMatchExpected(outFalse)("out" -> _.out)
+ }
+
+ "plugin should work with Bundles with Option fields" in {
+ ChiselStage.emitFirrtl(new UsesBundleWithOptionFields)
+ }
+
+ "plugin should work with enums in bundles" in {
+ object Enum0 extends ChiselEnum {
+ val s0, s1, s2 = Value
+ }
+
+ ChiselStage.emitFirrtl(new Module {
+ val out = IO(Output(new Bundle {
+ val a = UInt(8.W)
+ val b = Bool()
+ val c = Enum0.Type
+ }))
+ assertElementsMatchExpected(out)("b" -> _.b, "a" -> _.a)
+ })
+ }
+
+ "plugin will NOT see fields that are Data but declared in some way as Any" in {
+ //This is not incompatible with chisel not using the plugin, but this code is considered bad practice
+
+ ChiselStage.emitFirrtl(new Module {
+ val out = IO(Output(new Bundle {
+ val a = UInt(8.W)
+ val b: Any = Bool()
+ }))
+
+ intercept[TestFailedException] {
+ assert(out.elements.keys.exists(_ == "b"))
+ }
+ })
+ }
+
+ "plugin tests should fail properly in the following cases" - {
+
+ class BundleAB extends Bundle {
+ val a = Output(UInt(8.W))
+ val b = Output(Bool())
+ }
+
+ def checkAssertion(checks: (BundleAB => (String, Data))*)(expectedMessage: String): Unit = {
+ intercept[AssertionError] {
+ ChiselStage.emitFirrtl(new Module {
+ val out = IO(new BundleAB)
+ assertElementsMatchExpected(out)(checks: _*)
+ })
+ }.getMessage should include(expectedMessage)
+ }
+
+ "one of the expected data values is wrong" in {
+ checkAssertion("b" -> _.b, "a" -> _.b)("field 'a' data field BundleElementsSpec_Anon.out.a")
+ }
+
+ "one of the expected field names in wrong" in {
+ checkAssertion("b" -> _.b, "z" -> _.a)("field: 'a' did not match expected 'z'")
+ }
+
+ "fields that are expected are not returned by the elements method" in {
+ checkAssertion("b" -> _.b, "a" -> _.a, "c" -> _.a)("#elements is missing the 'c' field")
+ }
+
+ "fields returned by the element are not specified in the expected fields" in {
+ checkAssertion("b" -> _.b)("expected fields did not include 'a' field found in #elements")
+ }
+
+ "multiple errors between elements method and expected fields are shown in the assertion error message" in {
+ checkAssertion()(
+ "expected fields did not include 'b' field found in #elements," +
+ " expected fields did not include 'a' field found in #elements"
+ )
+ }
+ }
+
+ "plugin should error correctly when bundles contain only a Option field" in {
+ ChiselStage.emitFirrtl(new Module {
+ val io = IO(new Bundle {
+ val foo = Input(UInt(8.W))
+ val x = new Bundle {
+ val y = if (false) Some(Input(UInt(8.W))) else None
+ }
+ })
+ assertElementsMatchExpected(io)("x" -> _.x, "foo" -> _.foo)
+ assertElementsMatchExpected(io.x)()
+ })
+ }
+
+ "plugin should handle fields using the boolean to option construct" in {
+ case class ALUConfig(
+ xLen: Int,
+ mul: Boolean,
+ b: Boolean)
+
+ class OptionalBundle extends Bundle {
+ val optionBundleA = Input(UInt(3.W))
+ val optionBundleB = Input(UInt(7.W))
+ }
+
+ class ALU(c: ALUConfig) extends Module {
+
+ class BpipOptionBundle extends Bundle with IgnoreSeqInBundle {
+ val bpipUIntVal = Input(UInt(8.W))
+ lazy val bpipUIntLazyVal = Input(UInt(8.W))
+ var bpipUIntVar = Input(UInt(8.W))
+
+ def bpipUIntDef = Input(UInt(8.W))
+
+ val bpipOptionUInt = Some(Input(UInt(16.W)))
+ val bpipOptionOfBundle = if (c.b) Some(new OptionalBundle) else None
+ val bpipSeqData = Seq(UInt(8.W), UInt(8.W))
+ }
+
+ val io = IO(new BpipOptionBundle)
+ assertElementsMatchExpected(io)(
+ "bpipUIntLazyVal" -> _.bpipUIntLazyVal,
+ "bpipOptionUInt" -> _.bpipOptionUInt.get,
+ "bpipUIntVar" -> _.bpipUIntVar,
+ "bpipUIntVal" -> _.bpipUIntVal
+ )
+ }
+
+ ChiselStage.emitFirrtl(new ALU(ALUConfig(10, mul = true, b = false)))
+ }
+
+ "TraceSpec test, different version found in TraceSpec.scala" in {
+ class Bundle0 extends Bundle {
+ val a = UInt(8.W)
+ val b = Bool()
+ val c = Enum0.Type
+ }
+
+ class Bundle1 extends Bundle {
+ val a = new Bundle0
+ val b = Vec(4, Vec(4, Bool()))
+ }
+
+ class Module0 extends Module {
+ val i = IO(Input(new Bundle1))
+ val o = IO(Output(new Bundle1))
+ val r = Reg(new Bundle1)
+ o := r
+ r := i
+
+ assertElementsMatchExpected(i)("b" -> _.b, "a" -> _.a)
+ assertElementsMatchExpected(o)("b" -> _.b, "a" -> _.a)
+ assertElementsMatchExpected(r)("b" -> _.b, "a" -> _.a)
+ }
+
+ class Module1 extends Module {
+ val i = IO(Input(new Bundle1))
+ val m0 = Module(new Module0)
+ m0.i := i
+ m0.o := DontCare
+ assertElementsMatchExpected(i)("b" -> _.b, "a" -> _.a)
+ }
+
+ object Enum0 extends ChiselEnum {
+ val s0, s1, s2 = Value
+ }
+
+ ChiselStage.emitFirrtl(new Module1)
+ }
+}
+/* Checks that the elements method of a bundle matches the testers idea of what the bundle field names and their
+ * associated data match and that they have the same number of fields in the same order
+ */
+object assertElementsMatchExpected {
+ def apply[T <: Bundle](bun: T)(checks: (T => (String, Data))*): Unit = {
+ val expected = checks.map { fn => fn(bun) }
+ val elements = bun.elements
+ val missingMsg = "missing field in #elements"
+ val extraMsg = "extra field in #elements"
+ val paired = elements.toSeq.zipAll(expected, missingMsg -> UInt(1.W), extraMsg -> UInt(1.W))
+ val errorsStrings = paired.flatMap {
+ case (element, expected) =>
+ val (elementName, elementData) = element
+ val (expectedName, expectedData) = expected
+ if (elementName == missingMsg) {
+ Some(s"#elements is missing the '$expectedName' field")
+ } else if (expectedName == extraMsg) {
+ Some(s"expected fields did not include '$elementName' field found in #elements")
+ } else if (elementName != expectedName) {
+ Some(s"field: '$elementName' did not match expected '$expectedName'")
+ } else if (elementData != expectedData) {
+ Some(
+ s"field '$elementName' data field ${elementData}(${elementData.hashCode}) did not match expected $expectedData(${expectedData.hashCode})"
+ )
+ } else {
+ None
+ }
+ }
+ assert(errorsStrings.isEmpty, s"Bundle: ${bun.getClass.getName}: " + errorsStrings.mkString(", "))
+ }
+}
diff --git a/src/test/scala/chiselTests/BundleSpec.scala b/src/test/scala/chiselTests/BundleSpec.scala
index 720f877f..5dcbbefa 100644
--- a/src/test/scala/chiselTests/BundleSpec.scala
+++ b/src/test/scala/chiselTests/BundleSpec.scala
@@ -26,6 +26,10 @@ trait BundleSpecUtils {
val bar = Seq(UInt(16.W), UInt(8.W), UInt(4.W))
}
+ class BadSeqBundleWithIgnoreSeqInBundle extends Bundle with IgnoreSeqInBundle {
+ val bar = Seq(UInt(16.W), UInt(8.W), UInt(4.W))
+ }
+
class MyModule(output: Bundle, input: Bundle) extends Module {
val io = IO(new Bundle {
val in = Input(input)
@@ -87,7 +91,7 @@ class BundleSpec extends ChiselFlatSpec with BundleSpecUtils with Utils {
new BasicTester {
val m = Module(new Module {
val io = IO(new Bundle {
- val b = new BadSeqBundle with IgnoreSeqInBundle
+ val b = new BadSeqBundleWithIgnoreSeqInBundle
})
})
stop()
@@ -141,7 +145,7 @@ class BundleSpec extends ChiselFlatSpec with BundleSpecUtils with Utils {
out := in
}
}
- }).getMessage should include("must be a Chisel type, not hardware")
+ }).getMessage should include("MyBundle contains hardware fields: foo: UInt<7>(123)")
}
"Bundles" should "not recursively contain aggregates with bound hardware" in {
(the[ChiselException] thrownBy extractCause[ChiselException] {
@@ -153,7 +157,7 @@ class BundleSpec extends ChiselFlatSpec with BundleSpecUtils with Utils {
out := in
}
}
- }).getMessage should include("must be a Chisel type, not hardware")
+ }).getMessage should include("Bundle: MyBundle contains hardware fields: foo: BundleSpec_Anon.out")
}
"Unbound bundles sharing a field" should "not error" in {
ChiselStage.elaborate {
diff --git a/src/test/scala/chiselTests/CompatibilitySpec.scala b/src/test/scala/chiselTests/CompatibilitySpec.scala
index d134c380..41cfbec4 100644
--- a/src/test/scala/chiselTests/CompatibilitySpec.scala
+++ b/src/test/scala/chiselTests/CompatibilitySpec.scala
@@ -238,10 +238,10 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck
"A Module without val io" should "throw an exception" in {
class ModuleWithoutValIO extends Module {
- val foo = new Bundle {
+ val foo = IO(new Bundle {
val in = UInt(width = 32).asInput
val out = Bool().asOutput
- }
+ })
foo.out := foo.in(1)
}
val e = intercept[Exception](
@@ -595,4 +595,23 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck
verilog should include("output [7:0] io_bar")
}
+ it should "properly handle hardware construction before val io is initialized" in {
+ class MyModule extends Module {
+ val foo = Wire(init = UInt(8))
+ val io = new Bundle {
+ val in = UInt(INPUT, width = 8)
+ val en = Bool(INPUT)
+ val out = UInt(OUTPUT, width = 8)
+ }
+ io.out := foo
+ when(io.en) {
+ io.out := io.in
+ }
+ }
+ // Just check that this doesn't crash during elaboration. For more info see:
+ // https://github.com/chipsalliance/chisel3/issues/1802
+ //
+ ChiselStage.elaborate(new MyModule)
+ }
+
}
diff --git a/src/test/scala/chiselTests/MultiClockSpec.scala b/src/test/scala/chiselTests/MultiClockSpec.scala
index 2553f3b3..381b4009 100644
--- a/src/test/scala/chiselTests/MultiClockSpec.scala
+++ b/src/test/scala/chiselTests/MultiClockSpec.scala
@@ -113,7 +113,7 @@ class MultiClockMemTest extends BasicTester {
when(done) { stop() }
}
-class MultiClockSpec extends ChiselFlatSpec {
+class MultiClockSpec extends ChiselFlatSpec with Utils {
"withClock" should "scope the clock of registers" in {
assertTesterPasses(new ClockDividerTest)
@@ -129,6 +129,114 @@ class MultiClockSpec extends ChiselFlatSpec {
})
}
+ "Differing clocks at memory and port instantiation" should "warn" in {
+ class modMemDifferingClock extends Module {
+ val myClock = IO(Input(Clock()))
+ val mem = withClock(myClock) { Mem(4, UInt(8.W)) }
+ val port0 = mem(0.U)
+ }
+ val (logMemDifferingClock, _) = grabLog(ChiselStage.elaborate(new modMemDifferingClock))
+ logMemDifferingClock should include("memory is different")
+
+ class modSyncReadMemDifferingClock extends Module {
+ val myClock = IO(Input(Clock()))
+ val mem = withClock(myClock) { SyncReadMem(4, UInt(8.W)) }
+ val port0 = mem(0.U)
+ }
+ val (logSyncReadMemDifferingClock, _) = grabLog(ChiselStage.elaborate(new modSyncReadMemDifferingClock))
+ logSyncReadMemDifferingClock should include("memory is different")
+ }
+
+ "Differing clocks at memory and write accessor instantiation" should "warn" in {
+ class modMemWriteDifferingClock extends Module {
+ val myClock = IO(Input(Clock()))
+ val mem = withClock(myClock) { Mem(4, UInt(8.W)) }
+ mem(1.U) := 1.U
+ }
+ val (logMemWriteDifferingClock, _) = grabLog(ChiselStage.elaborate(new modMemWriteDifferingClock))
+ logMemWriteDifferingClock should include("memory is different")
+
+ class modSyncReadMemWriteDifferingClock extends Module {
+ val myClock = IO(Input(Clock()))
+ val mem = withClock(myClock) { SyncReadMem(4, UInt(8.W)) }
+ mem.write(1.U, 1.U)
+ }
+ val (logSyncReadMemWriteDifferingClock, _) = grabLog(ChiselStage.elaborate(new modSyncReadMemWriteDifferingClock))
+ logSyncReadMemWriteDifferingClock should include("memory is different")
+ }
+
+ "Differing clocks at memory and read accessor instantiation" should "warn" in {
+ class modMemReadDifferingClock extends Module {
+ val myClock = IO(Input(Clock()))
+ val mem = withClock(myClock) { Mem(4, UInt(8.W)) }
+ val readVal = mem.read(0.U)
+ }
+ val (logMemReadDifferingClock, _) = grabLog(ChiselStage.elaborate(new modMemReadDifferingClock))
+ logMemReadDifferingClock should include("memory is different")
+
+ class modSyncReadMemReadDifferingClock extends Module {
+ val myClock = IO(Input(Clock()))
+ val mem = withClock(myClock) { SyncReadMem(4, UInt(8.W)) }
+ val readVal = mem.read(0.U)
+ }
+ val (logSyncReadMemReadDifferingClock, _) = grabLog(ChiselStage.elaborate(new modSyncReadMemReadDifferingClock))
+ logSyncReadMemReadDifferingClock should include("memory is different")
+ }
+
+ "Passing clock parameter to memory port instantiation" should "not warn" in {
+ class modMemPortClock extends Module {
+ val myClock = IO(Input(Clock()))
+ val mem = Mem(4, UInt(8.W))
+ val port0 = mem(0.U, myClock)
+ }
+ val (logMemPortClock, _) = grabLog(ChiselStage.elaborate(new modMemPortClock))
+ (logMemPortClock should not).include("memory is different")
+
+ class modSyncReadMemPortClock extends Module {
+ val myClock = IO(Input(Clock()))
+ val mem = SyncReadMem(4, UInt(8.W))
+ val port0 = mem(0.U, myClock)
+ }
+ val (logSyncReadMemPortClock, _) = grabLog(ChiselStage.elaborate(new modSyncReadMemPortClock))
+ (logSyncReadMemPortClock should not).include("memory is different")
+ }
+
+ "Passing clock parameter to memory write accessor" should "not warn" in {
+ class modMemWriteClock extends Module {
+ val myClock = IO(Input(Clock()))
+ val mem = Mem(4, UInt(8.W))
+ mem.write(0.U, 0.U, myClock)
+ }
+ val (logMemWriteClock, _) = grabLog(ChiselStage.elaborate(new modMemWriteClock))
+ (logMemWriteClock should not).include("memory is different")
+
+ class modSyncReadMemWriteClock extends Module {
+ val myClock = IO(Input(Clock()))
+ val mem = SyncReadMem(4, UInt(8.W))
+ mem.write(0.U, 0.U, myClock)
+ }
+ val (logSyncReadMemWriteClock, _) = grabLog(ChiselStage.elaborate(new modSyncReadMemWriteClock))
+ (logSyncReadMemWriteClock should not).include("memory is different")
+ }
+
+ "Passing clock parameter to memory read accessor" should "not warn" in {
+ class modMemReadClock extends Module {
+ val myClock = IO(Input(Clock()))
+ val mem = Mem(4, UInt(8.W))
+ val readVal = mem.read(0.U, myClock)
+ }
+ val (logMemReadClock, _) = grabLog(ChiselStage.elaborate(new modMemReadClock))
+ (logMemReadClock should not).include("memory is different")
+
+ class modSyncReadMemReadClock extends Module {
+ val myClock = IO(Input(Clock()))
+ val mem = SyncReadMem(4, UInt(8.W))
+ val readVal = mem.read(0.U, myClock)
+ }
+ val (logSyncReadMemReadClock, _) = grabLog(ChiselStage.elaborate(new modSyncReadMemReadClock))
+ (logSyncReadMemReadClock should not).include("memory is different")
+ }
+
"withReset" should "scope the reset of registers" in {
assertTesterPasses(new WithResetTest)
}
diff --git a/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala b/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala
index f62d1e49..45d1f85f 100644
--- a/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala
+++ b/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala
@@ -298,6 +298,28 @@ class InstanceSpec extends ChiselFunSpec with Utils {
annos should contain(MarkAnnotation("~Top|Top/i:HasEither>x".rt, "xright"))
annos should contain(MarkAnnotation("~Top|Top/i:HasEither>y".rt, "yleft"))
}
+ it("3.12: should properly support val modifiers") {
+ class SupClass extends Module {
+ val value = 10
+ val overriddenVal = 10
+ }
+ trait SupTrait {
+ def x: Int
+ def y: Int
+ }
+ @instantiable class SubClass() extends SupClass with SupTrait {
+ // This errors
+ //@public private val privateVal = 10
+ // This errors
+ //@public protected val protectedVal = 10
+ @public override val overriddenVal = 12
+ @public final val finalVal = 12
+ @public lazy val lazyValue = 12
+ @public val value = value
+ @public final override lazy val x: Int = 3
+ @public override final lazy val y: Int = 4
+ }
+ }
}
describe("4: toInstance") {
it("4.0: should work on modules") {
diff --git a/src/test/scala/chiselTests/stage/ChiselStageSpec.scala b/src/test/scala/chiselTests/stage/ChiselStageSpec.scala
index e88a2385..f0f383da 100644
--- a/src/test/scala/chiselTests/stage/ChiselStageSpec.scala
+++ b/src/test/scala/chiselTests/stage/ChiselStageSpec.scala
@@ -34,6 +34,14 @@ object ChiselStageSpec {
assert(false, "User threw an exception")
}
+ class UserExceptionNoStackTrace extends RawModule {
+ throw new Exception("Something bad happened") with scala.util.control.NoStackTrace
+ }
+
+ class RecoverableError extends RawModule {
+ 3.U >> -1
+ }
+
}
class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils {
@@ -169,6 +177,32 @@ class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils {
(exception.getStackTrace.mkString("\n") should not).include("java")
}
+ it should "NOT add a stack trace to an exception with no stack trace" in {
+ val exception = intercept[java.lang.Exception] {
+ ChiselStage.emitChirrtl(new UserExceptionNoStackTrace)
+ }
+
+ val message = exception.getMessage
+ info("The exception includes the user's message")
+ message should include("Something bad happened")
+
+ info("The exception should not contain a stack trace")
+ exception.getStackTrace should be(Array())
+ }
+
+ it should "NOT include a stack trace for recoverable errors" in {
+ val exception = intercept[java.lang.Exception] {
+ ChiselStage.emitChirrtl(new RecoverableError)
+ }
+
+ val message = exception.getMessage
+ info("The exception includes the standard error message")
+ message should include("Fatal errors during hardware elaboration. Look above for error list.")
+
+ info("The exception should not contain a stack trace")
+ exception.getStackTrace should be(Array())
+ }
+
behavior.of("ChiselStage exception handling")
it should "truncate a user exception" in {
@@ -207,4 +241,53 @@ class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils {
exception.getStackTrace.mkString("\n") should include("java")
}
+ it should "NOT add a stack trace to an exception with no stack trace" in {
+ val exception = intercept[java.lang.Exception] {
+ (new ChiselStage).emitChirrtl(new UserExceptionNoStackTrace)
+ }
+
+ val message = exception.getMessage
+ info("The exception includes the user's message")
+ message should include("Something bad happened")
+
+ info("The exception should not contain a stack trace")
+ exception.getStackTrace should be(Array())
+ }
+
+ it should "NOT include a stack trace for recoverable errors" in {
+ val exception = intercept[java.lang.Exception] {
+ (new ChiselStage).emitChirrtl(new RecoverableError)
+ }
+
+ val message = exception.getMessage
+ info("The exception includes the standard error message")
+ message should include("Fatal errors during hardware elaboration. Look above for error list.")
+
+ info("The exception should not contain a stack trace")
+ exception.getStackTrace should be(Array())
+ }
+
+ it should "include a stack trace for recoverable errors with '--throw-on-first-error'" in {
+ val exception = intercept[java.lang.Exception] {
+ (new ChiselStage).emitChirrtl(new RecoverableError, Array("--throw-on-first-error"))
+ }
+
+ val stackTrace = exception.getStackTrace.mkString("\n")
+ info("The exception should contain a truncated stack trace")
+ stackTrace shouldNot include("java")
+
+ info("The stack trace include information about running --full-stacktrace")
+ stackTrace should include("--full-stacktrace")
+ }
+
+ it should "include an untruncated stack trace for recoverable errors when given both '--throw-on-first-error' and '--full-stacktrace'" in {
+ val exception = intercept[java.lang.Exception] {
+ val args = Array("--throw-on-first-error", "--full-stacktrace")
+ (new ChiselStage).emitChirrtl(new RecoverableError, args)
+ }
+
+ val stackTrace = exception.getStackTrace.mkString("\n")
+ info("The exception should contain a truncated stack trace")
+ stackTrace should include("java")
+ }
}