summaryrefslogtreecommitdiff
path: root/src/test/scala/chiselTests/RecordSpec.scala
diff options
context:
space:
mode:
authorJack2022-11-11 06:53:04 +0000
committerJack2022-11-11 06:53:04 +0000
commit3ce953c81f06519351c48277e3474b5720ec07ff (patch)
treeac79dcb80d0528c2ae86ca21da4cf424715ab645 /src/test/scala/chiselTests/RecordSpec.scala
parentadccde9998c91875e5490cff6d5822ffacc593ed (diff)
parentc8046636a25474be4c547c6fe9c6d742ea7b1d13 (diff)
Merge branch '3.5.x' into 3.5-release
Diffstat (limited to 'src/test/scala/chiselTests/RecordSpec.scala')
-rw-r--r--src/test/scala/chiselTests/RecordSpec.scala213
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")
+ }
}