diff --git a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala index 8700b44423..00d55dde57 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala @@ -15,9 +15,42 @@ import chisel3.internal.sourceinfo._ * of) other Data objects. */ sealed abstract class Aggregate extends Data { + protected def childLitArgs = getElements.map(_.litArg).foldLeft[Option[Seq[LitArg]]](Some(Seq[LitArg]())) { + case (Some(elems), Some(lit)) => Some(elems :+ lit) + case _ => None + } + override def litArg = { + // TODO[grebe] make a lit for Aggregates + childLitArgs.map { x => ULit(0, 1.W) } + } + + private var checkedBinding = false + private def checkBinding = { + // check if this aggregate is a literal, and bind it if it hasn't been bound yet + if (!checkedBinding && getElements.length > 0 && childLitArgs.isDefined) { + this.bind(LitBinding()) //, SpecifiedDirection.Unspecified) + } + checkedBinding = true + } + + protected[core] override def hasBinding = { + checkBinding + super.hasBinding + } + + protected[core] override def binding = { + checkBinding + super.binding + } + private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection) { binding = target + if (isLit) { + getElements.foreach { elem => require(elem.isLit()) } + return () + } + val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection) for (child <- getElements) { child.bind(ChildBinding(this), resolvedDirection) @@ -448,6 +481,11 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio private[chisel3] final def allElements: Seq[Element] = elements.toIndexedSeq.flatMap(_._2.allElements) + protected override def childLitArgs = elements.map({ case (_, data) => data.litArg }).foldLeft[Option[Seq[LitArg]]](Some(Seq[LitArg]())) { + case (Some(elems), Some(lit)) => Some(elems :+ lit) + case _ => None + } + override def getElements: Seq[Data] = elements.toIndexedSeq.map(_._2) // Helper because Bundle elements are reversed before printing @@ -553,7 +591,7 @@ class Bundle(implicit compileOptions: CompileOptions) extends Record { // - A one-parameter constructor for a nested Bundle, with the enclosing // parent Module as the argument val constructor = this.getClass.getConstructors.head - try { + val newBundle = try { val args = Seq.fill(constructor.getParameterTypes.size)(null) constructor.newInstance(args:_*).asInstanceOf[this.type] } catch { @@ -570,6 +608,10 @@ class Bundle(implicit compileOptions: CompileOptions) extends Record { Builder.exception(s"Parameterized Bundle ${this.getClass} needs cloneType method") this } + + require(!newBundle.isLit, s"Bundle ${this.getClass} has a bad cloneType: it should never return a literal") + + newBundle.asInstanceOf[this.type] } /** Default "pretty-print" implementation diff --git a/src/main/scala/chisel3/testers/BasicTester.scala b/src/main/scala/chisel3/testers/BasicTester.scala index 6d1a49132d..64ab810344 100644 --- a/src/main/scala/chisel3/testers/BasicTester.scala +++ b/src/main/scala/chisel3/testers/BasicTester.scala @@ -13,7 +13,7 @@ import internal.sourceinfo.SourceInfo class BasicTester extends Module() { // The testbench has no IOs, rather it should communicate using printf, assert, and stop. - val io = IO(new Bundle()) + val io = IO(new Bundle {val dummy = Input(UInt(0.W))}) def popCount(n: Long): Int = n.toBinaryString.count(_=='1') diff --git a/src/test/scala/chiselTests/BundleLiterals.scala b/src/test/scala/chiselTests/BundleLiterals.scala new file mode 100644 index 0000000000..a8ad4f866a --- /dev/null +++ b/src/test/scala/chiselTests/BundleLiterals.scala @@ -0,0 +1,122 @@ +// See LICENSE for license details. + +package chiselTests + +import chisel3._ +import org.scalatest._ + +// Simple bundle which might be a literal. +class MyPair(xLit: Option[Int] = None, yLit: Option[Int] = None) extends Bundle { + val x: UInt = xLit match { + case Some(l) => l.U(4.W) + case _ => UInt(4.W) + } + val y: UInt = yLit match { + case Some(l) => l.U(4.W) + case _ => UInt(4.W) + } + + def +(that: MyPair): MyPair = { + val output = Wire(MyPair()) + output.x := x + that.x + output.y := y + that.y + output + } + + override def cloneType = (new MyPair(xLit, yLit)).asInstanceOf[this.type] +} + +object MyPair { + // Create an unbound pair + def apply(): MyPair = new MyPair() + + // Create a literal pair + def apply(x: Int, y: Int): MyPair = { + new MyPair(Some(x), Some(y)) + } +} + +// Example usecase of a user might use the MyPair type +class PairModule extends Module { + val io = IO(new Bundle{ + val in = Input(MyPair()) + val plus_one = Output(MyPair()) + val vanilla = Output(MyPair()) + }) + val not_a_lit = new MyPair(Some(1)) + assert(!not_a_lit.isLit) + val only_a_type = new MyPair() + assert(!only_a_type.isLit) + + val one = MyPair(1, 1) + assert(one.isLit) + io.vanilla := io.in + io.plus_one := io.in + one +} + +case class BundleWithUInt(x: UInt = UInt()) extends Bundle { + override def cloneType = BundleWithUInt(x.cloneType).asInstanceOf[this.type] +} + +case class BundleWithBundleWithUInt(x: BundleWithUInt = BundleWithUInt()) extends Bundle { + override def cloneType = BundleWithBundleWithUInt(x.cloneType).asInstanceOf[this.type] +} + +case class BundleWithUIntAndBadCloneType(x: UInt = UInt()) extends Bundle + + +class BundleWithUIntModule extends Module { + val io = IO(new Bundle { + val out = Output(BundleWithUInt(UInt(4.W))) + }) + io.out := BundleWithUInt(10.U) +} + +class BundleWithCloneTypeModule extends Module { + val io = IO(new Bundle { + val out = Output(BundleWithUInt(9.U)) // will call cloneType + }) + io.out := BundleWithUInt(5.U) +} + +class BundleWithBadCloneTypeModule extends Module { + val io = IO(new Bundle { + val out = Output(BundleWithUIntAndBadCloneType(3.U)) + }) + io.out := BundleWithUIntAndBadCloneType(0.U) +} + +class BundleWithBundleWithUIntModule extends Module { + val io = IO(new Bundle { + val out = Output(BundleWithBundleWithUInt(BundleWithUInt(UInt(4.W)))) + }) + io.out := BundleWithBundleWithUInt(BundleWithUInt(7.U)) +} + +class BundleLiteralsSpec extends FlatSpec with Matchers { + behavior of "Bundle literals" + + it should "check that all elements are bound and work as expected" in { + println(chisel3.Driver.emitVerilog( new PairModule )) + } + + it should "build the module without crashing" in { + println(chisel3.Driver.emitVerilog( new BundleWithUIntModule )) + } + + it should "work with a correct cloneType implementation" in { + println(chisel3.Driver.emitVerilog( new BundleWithCloneTypeModule )) + } + + it should "throw an exception if cloneType is bad" in { + assertThrows[IllegalArgumentException] { + println(chisel3.Driver.emitVerilog( new BundleWithBadCloneTypeModule )) + } + } + + behavior of "Bundle literals with bundle literals inside" + + it should "build the module without crashing" in { + println(chisel3.Driver.emitVerilog( new BundleWithBundleWithUIntModule )) + } +}