diff options
Diffstat (limited to 'src/test/scala/chiselTests/RecordSpec.scala')
| -rw-r--r-- | src/test/scala/chiselTests/RecordSpec.scala | 213 |
1 files changed, 211 insertions, 2 deletions
diff --git a/src/test/scala/chiselTests/RecordSpec.scala b/src/test/scala/chiselTests/RecordSpec.scala index 30b55812..3414ec8a 100644 --- a/src/test/scala/chiselTests/RecordSpec.scala +++ b/src/test/scala/chiselTests/RecordSpec.scala @@ -6,7 +6,7 @@ import chisel3._ import chisel3.stage.ChiselStage import chisel3.testers.BasicTester import chisel3.util.{Counter, Queue} -import chisel3.experimental.DataMirror +import chisel3.experimental.{DataMirror, OpaqueType} import scala.collection.immutable.SeqMap @@ -108,6 +108,123 @@ trait RecordSpecUtils { require(DataMirror.checkTypeEquivalence(wire0, wire1)) require(!DataMirror.checkTypeEquivalence(wire1, wire2)) } + + class SingleElementRecord extends Record with OpaqueType { + private val underlying = UInt(8.W) + val elements = SeqMap("" -> underlying) + override def cloneType: this.type = (new SingleElementRecord).asInstanceOf[this.type] + + def +(that: SingleElementRecord): SingleElementRecord = { + val _w = Wire(new SingleElementRecord) + _w.underlying := this.underlying + that.underlying + _w + } + } + + class SingleElementRecordModule extends Module { + val in1 = IO(Input(new SingleElementRecord)) + val in2 = IO(Input(new SingleElementRecord)) + val out = IO(Output(new SingleElementRecord)) + + val r = new SingleElementRecord + + out := in1 + in2 + } + + class InnerRecord extends Record with OpaqueType { + val k = new InnerInnerRecord + val elements = SeqMap("" -> k) + override def cloneType: this.type = (new InnerRecord).asInstanceOf[this.type] + } + + class InnerInnerRecord extends Record with OpaqueType { + val k = new SingleElementRecord + val elements = SeqMap("" -> k) + override def cloneType: this.type = (new InnerInnerRecord).asInstanceOf[this.type] + } + + class NestedRecordModule extends Module { + val in = IO(Input(new InnerRecord)) + val out = IO(Output(new InnerRecord)) + val inst = Module(new InnerModule) + inst.io.foo := in + out := inst.io.bar + } + class InnerModule extends Module { + val io = IO(new Bundle { + val foo = Input(new InnerRecord) + val bar = Output(new InnerRecord) + }) + + // DO NOT do this; just for testing element connections + io.bar.elements.head._2 := io.foo.elements.head._2 + } + + class NamedSingleElementRecord extends Record with OpaqueType { + private val underlying = UInt(8.W) + val elements = SeqMap("unused" -> underlying) + + override def cloneType: this.type = (new NamedSingleElementRecord).asInstanceOf[this.type] + } + + class NamedSingleElementModule extends Module { + val in = IO(Input(new NamedSingleElementRecord)) + val out = IO(Output(new NamedSingleElementRecord)) + out := in + } + + class ErroneousOverride extends Record with OpaqueType { + private val underlyingA = UInt(8.W) + private val underlyingB = UInt(8.W) + val elements = SeqMap("x" -> underlyingA, "y" -> underlyingB) + + override def opaqueType = true + override def cloneType: this.type = (new ErroneousOverride).asInstanceOf[this.type] + } + + class ErroneousOverrideModule extends Module { + val in = IO(Input(new ErroneousOverride)) + val out = IO(Output(new ErroneousOverride)) + out := in + } + + class NotActuallyOpaqueType extends Record with OpaqueType { + private val underlyingA = UInt(8.W) + private val underlyingB = UInt(8.W) + val elements = SeqMap("x" -> underlyingA, "y" -> underlyingB) + + override def opaqueType = false + override def cloneType: this.type = (new NotActuallyOpaqueType).asInstanceOf[this.type] + } + + class NotActuallyOpaqueTypeModule extends Module { + val in = IO(Input(new NotActuallyOpaqueType)) + val out = IO(Output(new NotActuallyOpaqueType)) + out := in + } + + // Illustrate how to dyanmically decide between OpaqueType or not + sealed trait MaybeBoxed[T <: Data] extends Record { + def underlying: T + def boxed: Boolean + } + object MaybeBoxed { + def apply[T <: Data](gen: T, boxed: Boolean): MaybeBoxed[T] = { + if (boxed) new Boxed(gen) else new Unboxed(gen) + } + } + class Boxed[T <: Data](gen: T) extends MaybeBoxed[T] { + def boxed = true + lazy val elements = SeqMap("underlying" -> gen.cloneType) + def underlying = elements.head._2 + override def cloneType: this.type = (new Boxed(gen)).asInstanceOf[this.type] + } + class Unboxed[T <: Data](gen: T) extends MaybeBoxed[T] with OpaqueType { + def boxed = false + lazy val elements = SeqMap("" -> gen.cloneType) + def underlying = elements.head._2 + override def cloneType: this.type = (new Unboxed(gen)).asInstanceOf[this.type] + } } class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { @@ -133,7 +250,7 @@ class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { class AliasedFieldRecord extends Record { val foo = UInt(8.W) val elements = SeqMap("foo" -> foo, "bar" -> foo) - override def cloneType: AliasedFieldRecord.this.type = this + override def cloneType: AliasedFieldRecord.this.type = (new AliasedFieldRecord).asInstanceOf[this.type] } val e = intercept[AliasedAggregateFieldException] { @@ -146,6 +263,85 @@ class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { e.getMessage should include("contains aliased fields named (bar,foo)") } + they should "support OpaqueType for maps with single unnamed elements" in { + val singleElementChirrtl = ChiselStage.emitChirrtl { new SingleElementRecordModule } + singleElementChirrtl should include("input in1 : UInt<8>") + singleElementChirrtl should include("input in2 : UInt<8>") + singleElementChirrtl should include("add(in1, in2)") + } + + they should "work correctly for toTarget in nested OpaqueType Records" in { + var mod: NestedRecordModule = null + ChiselStage.elaborate { mod = new NestedRecordModule; mod } + val testStrings = Seq( + mod.inst.io.foo.toTarget.serialize, + mod.inst.io.foo.k.toTarget.serialize, + mod.inst.io.foo.k.k.toTarget.serialize, + mod.inst.io.foo.elements.head._2.toTarget.serialize, + mod.inst.io.foo.k.elements.head._2.toTarget.serialize, + mod.inst.io.foo.k.k.elements.head._2.toTarget.serialize + ) + testStrings.foreach(x => assert(x == "~NestedRecordModule|InnerModule>io.foo")) + } + + they should "work correctly when connecting nested OpaqueType elements" in { + val nestedRecordChirrtl = ChiselStage.emitChirrtl { new NestedRecordModule } + nestedRecordChirrtl should include("input in : UInt<8>") + nestedRecordChirrtl should include("output out : UInt<8>") + nestedRecordChirrtl should include("inst.io.foo <= in") + nestedRecordChirrtl should include("out <= inst.io.bar") + nestedRecordChirrtl should include("output io : { flip foo : UInt<8>, bar : UInt<8>}") + nestedRecordChirrtl should include("io.bar <= io.foo") + } + + they should "throw an error when map contains a named element and OpaqueType is mixed in" in { + (the[Exception] thrownBy extractCause[Exception] { + ChiselStage.elaborate { new NamedSingleElementModule } + }).getMessage should include("Opaque types must have exactly one element with an empty name") + } + + they should "throw an error when map contains more than one element and OpaqueType is mixed in" in { + (the[Exception] thrownBy extractCause[Exception] { + ChiselStage.elaborate { new ErroneousOverrideModule } + }).getMessage should include("Opaque types must have exactly one element with an empty name") + } + + they should "work correctly when an OpaqueType overrides the def as false" in { + val chirrtl = ChiselStage.emitChirrtl(new NotActuallyOpaqueTypeModule) + chirrtl should include("input in : { y : UInt<8>, x : UInt<8>}") + chirrtl should include("output out : { y : UInt<8>, x : UInt<8>}") + chirrtl should include("out <= in") + } + + they should "support conditional OpaqueTypes via traits and factory methods" in { + class MyModule extends Module { + val in0 = IO(Input(MaybeBoxed(UInt(8.W), true))) + val out0 = IO(Output(MaybeBoxed(UInt(8.W), true))) + val in1 = IO(Input(MaybeBoxed(UInt(8.W), false))) + val out1 = IO(Output(MaybeBoxed(UInt(8.W), false))) + out0 := in0 + out1 := in1 + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + chirrtl should include("input in0 : { underlying : UInt<8>}") + chirrtl should include("input in1 : UInt<8>") + } + + they should "work with .toTarget" in { + var m: SingleElementRecordModule = null + ChiselStage.elaborate { m = new SingleElementRecordModule; m } + val q = m.in1.toTarget.toString + assert(q == "~SingleElementRecordModule|SingleElementRecordModule>in1") + } + + they should "work (but warn) with .toTarget on non-data OpaqueType Record" in { + var m: SingleElementRecordModule = null + ChiselStage.elaborate { m = new SingleElementRecordModule; m } + val (log, q) = grabLog(m.r.toTarget) + log should include(".toTarget of non-hardware Data is deprecated") + assert(q.toString == "~SingleElementRecordModule|SingleElementRecordModule>r") + } + they should "follow UInt serialization/deserialization API" in { assertTesterPasses { new RecordSerializationTest } } @@ -184,4 +380,17 @@ class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { "CustomBundle" should "check the types" in { ChiselStage.elaborate { new RecordTypeTester } } + + "Record with unstable elements" should "error" in { + class MyRecord extends Record { + def elements = SeqMap("a" -> UInt(8.W)) + override def cloneType: this.type = (new MyRecord).asInstanceOf[this.type] + } + val e = the[ChiselException] thrownBy { + ChiselStage.elaborate(new Module { + val io = IO(Input(new MyRecord)) + }) + } + e.getMessage should include("does not return the same objects when calling .elements multiple times") + } } |
