summaryrefslogtreecommitdiff
path: root/src/test/scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/scala')
-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
6 files changed, 806 insertions, 6 deletions
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")
+ }
}