diff options
| author | Jack Koenig | 2017-01-20 12:37:40 -0800 |
|---|---|---|
| committer | GitHub | 2017-01-20 12:37:40 -0800 |
| commit | 2e6444cc55b54b59f781a14823e219d9a2413f72 (patch) | |
| tree | 0affb83730db42e41df214e8095d1ff1dd02b162 /src | |
| parent | 4512aeeacf594349cf9a816384e92bf3da63892f (diff) | |
Add Record as new superclass of Bundle (#366)
Record gives uses the power to create collections of heterogenous elements.
Bundle is a special case of Record that uses reflection to populate the
elements of the collection. Bundle also attempts to implement cloneType whereas
users of Record are required to supply one.
Diffstat (limited to 'src')
| -rw-r--r-- | src/main/scala/chisel3/compatibility.scala | 1 | ||||
| -rw-r--r-- | src/main/scala/chisel3/package.scala | 1 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/BundleSpec.scala | 71 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/RecordSpec.scala | 119 |
4 files changed, 192 insertions, 0 deletions
diff --git a/src/main/scala/chisel3/compatibility.scala b/src/main/scala/chisel3/compatibility.scala index 613385af..abbe8ffe 100644 --- a/src/main/scala/chisel3/compatibility.scala +++ b/src/main/scala/chisel3/compatibility.scala @@ -36,6 +36,7 @@ package object Chisel { // scalastyle:ignore package.object.name val Vec = chisel3.core.Vec type Vec[T <: Data] = chisel3.core.Vec[T] type VecLike[T <: Data] = chisel3.core.VecLike[T] + type Record = chisel3.core.Record type Bundle = chisel3.core.Bundle val assert = chisel3.core.assert diff --git a/src/main/scala/chisel3/package.scala b/src/main/scala/chisel3/package.scala index 29095243..cba8dffe 100644 --- a/src/main/scala/chisel3/package.scala +++ b/src/main/scala/chisel3/package.scala @@ -26,6 +26,7 @@ package object chisel3 { // scalastyle:ignore package.object.name type Vec[T <: Data] = chisel3.core.Vec[T] type VecLike[T <: Data] = chisel3.core.VecLike[T] type Bundle = chisel3.core.Bundle + type Record = chisel3.core.Record val assert = chisel3.core.assert diff --git a/src/test/scala/chiselTests/BundleSpec.scala b/src/test/scala/chiselTests/BundleSpec.scala new file mode 100644 index 00000000..0a6866d3 --- /dev/null +++ b/src/test/scala/chiselTests/BundleSpec.scala @@ -0,0 +1,71 @@ +// See LICENSE for license details. + +package chiselTests + +import chisel3._ +import chisel3.testers.BasicTester + +trait BundleSpecUtils { + class BundleFooBar extends Bundle { + val foo = UInt(16.W) + val bar = UInt(16.W) + override def cloneType = (new BundleFooBar).asInstanceOf[this.type] + } + class BundleBarFoo extends Bundle { + val bar = UInt(16.W) + val foo = UInt(16.W) + override def cloneType = (new BundleBarFoo).asInstanceOf[this.type] + } + class BundleFoo extends Bundle { + val foo = UInt(16.W) + override def cloneType = (new BundleFoo).asInstanceOf[this.type] + } + class BundleBar extends Bundle { + val bar = UInt(16.W) + override def cloneType = (new BundleBar).asInstanceOf[this.type] + } + + class MyModule(output: Bundle, input: Bundle) extends Module { + val io = IO(new Bundle { + val in = Input(input) + val out = Output(output) + }) + io.out <> io.in + } + + class BundleSerializationTest extends BasicTester { + // Note that foo is higher order because its defined earlier in the Bundle + val bundle = Wire(new BundleFooBar) + bundle.foo := 0x1234.U + bundle.bar := 0x5678.U + // To UInt + val uint = bundle.asUInt + assert(uint.getWidth == 32) // elaboration time + assert(uint === "h12345678".asUInt(32.W)) + // Back to Bundle + val bundle2 = (new BundleFooBar).fromBits(uint) + assert(0x1234.U === bundle2.foo) + assert(0x5678.U === bundle2.bar) + stop() + } +} + +class BundleSpec extends ChiselFlatSpec with BundleSpecUtils { + "Bundles with the same fields but in different orders" should "bulk connect" in { + elaborate { new MyModule(new BundleFooBar, new BundleBarFoo) } + } + + "Bundles" should "follow UInt serialization/deserialization API" in { + assertTesterPasses { new BundleSerializationTest } + } + + "Bulk connect on Bundles" should "check that the fields match" in { + (the [ChiselException] thrownBy { + elaborate { new MyModule(new BundleFooBar, new BundleFoo) } + }).getMessage should include ("Right Record missing field") + + (the [ChiselException] thrownBy { + elaborate { new MyModule(new BundleFoo, new BundleFooBar) } + }).getMessage should include ("Left Record missing field") + } +} diff --git a/src/test/scala/chiselTests/RecordSpec.scala b/src/test/scala/chiselTests/RecordSpec.scala new file mode 100644 index 00000000..c65693ed --- /dev/null +++ b/src/test/scala/chiselTests/RecordSpec.scala @@ -0,0 +1,119 @@ +// See LICENSE for license details. + +package chiselTests + +import chisel3._ +import chisel3.testers.BasicTester +import chisel3.util.{Counter, Queue} +import scala.collection.immutable.ListMap + +trait RecordSpecUtils { + // An example of how Record might be extended + // In this case, CustomBundle is a Record constructed from a Tuple of (String, Data) + // it is a possible implementation of a programmatic "Bundle" + // (and can by connected to MyBundle below) + final class CustomBundle(elts: (String, Data)*) extends Record { + val elements = ListMap(elts map { case (field, elt) => field -> elt.chiselCloneType }: _*) + def apply(elt: String): Data = elements(elt) + override def cloneType = (new CustomBundle(elements.toList: _*)).asInstanceOf[this.type] + } + class MyBundle extends Bundle { + val foo = UInt(32.W) + val bar = UInt(32.W) + override def cloneType = (new MyBundle).asInstanceOf[this.type] + } + // Useful for constructing types from CustomBundle + val fooBarType = new CustomBundle("foo" -> UInt(32.W), "bar" -> UInt(32.W)) + + class MyModule(output: => Record, input: => Record) extends Module { + val io = IO(new Bundle { + val in = Input(input) + val out = Output(output) + }) + io.out <> io.in + } + + class RecordSerializationTest extends BasicTester { + val recordType = new CustomBundle("fizz" -> UInt(16.W), "buzz" -> UInt(16.W)) + val record = Wire(recordType) + // Note that "buzz" was added later than "fizz" and is therefore higher order + record("fizz") := "hdead".U + record("buzz") := "hbeef".U + // To UInt + val uint = record.asUInt + assert(uint.getWidth == 32) // elaboration time + assert(uint === "hbeefdead".U) + // Back to Record + val record2 = recordType.fromBits(uint) + assert("hdead".U === record2("fizz").asUInt) + assert("hbeef".U === record2("buzz").asUInt) + stop() + } + + class RecordQueueTester extends BasicTester { + val queue = Module(new Queue(fooBarType, 4)) + queue.io.enq.valid := false.B + val (cycle, done) = Counter(true.B, 4) + + when (cycle === 0.U) { + queue.io.enq.bits("foo") := 1234.U + queue.io.enq.bits("bar") := 5678.U + queue.io.enq.valid := true.B + } + when (cycle === 1.U) { + queue.io.deq.ready := true.B + assert(queue.io.deq.valid === true.B) + assert(queue.io.deq.bits("foo").asUInt === 1234.U) + assert(queue.io.deq.bits("bar").asUInt === 5678.U) + } + when (done) { + stop() + } + } + + class RecordIOModule extends Module { + val io = IO(new CustomBundle("in" -> Input(UInt(32.W)), "out" -> Output(UInt(32.W)))) + io("out") := io("in") + } + + class RecordIOTester extends BasicTester { + val mod = Module(new RecordIOModule) + mod.io("in") := 1234.U + assert(mod.io("out").asUInt === 1234.U) + stop() + } +} + +class RecordSpec extends ChiselFlatSpec with RecordSpecUtils { + behavior of "Records" + + they should "bulk connect similarly to Bundles" in { + elaborate { new MyModule(fooBarType, fooBarType) } + } + + they should "bulk connect to Bundles" in { + elaborate { new MyModule(new MyBundle, fooBarType) } + } + + they should "follow UInt serialization/deserialization API" in { + assertTesterPasses { new RecordSerializationTest } + } + + they should "work as the type of a Queue" in { + assertTesterPasses { new RecordQueueTester } + } + + they should "work as the type of a Module's io" in { + assertTesterPasses { new RecordIOTester } + } + + "Bulk connect on Record" should "check that the fields match" in { + (the [ChiselException] thrownBy { + elaborate { new MyModule(fooBarType, new CustomBundle("bar" -> UInt(32.W))) } + }).getMessage should include ("Right Record missing field") + + (the [ChiselException] thrownBy { + elaborate { new MyModule(new CustomBundle("bar" -> UInt(32.W)), fooBarType) } + }).getMessage should include ("Left Record missing field") + } +} |
