diff options
Diffstat (limited to 'src/test')
| -rw-r--r-- | src/test/scala/chiselTests/BundleElementsSpec.scala | 564 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/BundleSpec.scala | 10 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/CompatibilitySpec.scala | 23 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/MultiClockSpec.scala | 110 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala | 22 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/stage/ChiselStageSpec.scala | 83 |
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") + } } |
