From 23ef9aa7ffef5bbf8fe124fc9be7683f005c3612 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Tue, 16 Aug 2022 19:04:28 +0000 Subject: Add OpaqueType support to Records (backport #2662) (#2679) * Add OpaqueType support to Records (#2662) OpaqueTypes are essentially type aliases that hide the underlying type. They are implemented in Chisel as Records of a single, unnamed element where the wrapping Record does not exist in the emitted FIRRTL. Co-authored-by: Jack Koenig (cherry picked from commit df5afee2d41b5bcd82d4013b977965c0dfe046fd) * Fix test compilation * Fix OpaqueType tests in RecordSpec Need to implement cloneType correctly and to warn instead of error when accessing .toTarget of non-hardware types because that is a warning (not error) in 3.5. * Waive MiMa false positives Co-authored-by: Aditya Naik <91489422+adkian-sifive@users.noreply.github.com> Co-authored-by: Jack Koenig --- src/test/scala/chiselTests/RecordSpec.scala | 88 ++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) (limited to 'src/test') diff --git a/src/test/scala/chiselTests/RecordSpec.scala b/src/test/scala/chiselTests/RecordSpec.scala index 30b55812..5aa872b0 100644 --- a/src/test/scala/chiselTests/RecordSpec.scala +++ b/src/test/scala/chiselTests/RecordSpec.scala @@ -108,6 +108,58 @@ trait RecordSpecUtils { require(DataMirror.checkTypeEquivalence(wire0, wire1)) require(!DataMirror.checkTypeEquivalence(wire1, wire2)) } + + class SingleElementRecord extends Record { + private val underlying = UInt(8.W) + val elements = SeqMap("" -> underlying) + override def opaqueType = elements.size == 1 + 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 NamedSingleElementRecord extends Record { + private val underlying = UInt(8.W) + val elements = SeqMap("unused" -> underlying) + + override def opaqueType = elements.size == 1 + 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 { + 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 RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { @@ -133,7 +185,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 +198,40 @@ class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { e.getMessage should include("contains aliased fields named (bar,foo)") } + they should "be 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 "throw an error when map contains a named element and opaqueType is overriden to true" 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 overriden to true" 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 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 } } -- cgit v1.2.3