summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala25
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Data.scala7
-rw-r--r--src/test/scala/chiselTests/AutoNestedCloneSpec.scala72
3 files changed, 104 insertions, 0 deletions
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
index d4d67018..da14cefd 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala
@@ -5,6 +5,7 @@ package chisel3.core
import scala.collection.immutable.ListMap
import scala.collection.mutable.{ArrayBuffer, HashSet, LinkedHashMap}
import scala.language.experimental.macros
+import scala.util.Try
import chisel3.internal._
import chisel3.internal.Builder.pushCommand
@@ -574,6 +575,30 @@ class Bundle(implicit compileOptions: CompileOptions) extends Record {
Builder.exception(s"Unable to automatically infer cloneType on $this: $desc")
}
+ // Check if the bundle is an instance of an inner class by examining
+ // whether it has one one-argument constructor taking a type matching the enclosing class
+ if (this.getClass.getConstructors.size == 1) {
+ val aggClass = this.getClass
+ val constr = aggClass.getConstructors.head
+ val argTypes = constr.getParameterTypes
+ val outerClass = aggClass.getEnclosingClass
+ if (argTypes.size == 1 && outerClass != null) {
+ // attempt to clone using "$outer"
+ var clone: Option[this.type] =
+ Try[this.type](constr.newInstance(aggClass.getDeclaredField("$outer").get(this)).asInstanceOf[this.type]).toOption
+ if (clone.isEmpty) {
+ // fall back to outerModule field
+ clone = Try[this.type](constr.newInstance(outerModule.get).asInstanceOf[this.type]).toOption
+ }
+ clone.foreach(_.outerModule = this.outerModule)
+ if (clone.isDefined) {
+ return clone.get
+ } else {
+ reflectError("non-trivial inner Bundle class")
+ }
+ }
+ }
+
// Try Scala reflection
import scala.reflect.runtime.universe._
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Data.scala b/chiselFrontend/src/main/scala/chisel3/core/Data.scala
index 19adf01b..d84a86e9 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Data.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Data.scala
@@ -208,6 +208,13 @@ abstract class Data extends HasId {
}
}
+ // If this Data is an instance of an inner class, record enclosing class
+ // This is only used for cloneType!
+ private[core] var outerModule: Option[BaseModule] =
+ (Option(this.getClass.getEnclosingClass) zip Builder.currentModule)
+ .find({ case (c, m) => c.isAssignableFrom(m.getClass) })
+ .map({ case (_, m) => m })
+
// User-specified direction, local at this node only.
// Note that the actual direction of this node can differ from child and parent specifiedDirection.
private var _specifiedDirection: SpecifiedDirection = SpecifiedDirection.Unspecified
diff --git a/src/test/scala/chiselTests/AutoNestedCloneSpec.scala b/src/test/scala/chiselTests/AutoNestedCloneSpec.scala
new file mode 100644
index 00000000..178cb142
--- /dev/null
+++ b/src/test/scala/chiselTests/AutoNestedCloneSpec.scala
@@ -0,0 +1,72 @@
+// See LICENSE for license details.
+
+package chiselTests
+import Chisel.ChiselException
+import org.scalatest._
+import chisel3._
+
+class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers {
+ behavior of "autoCloneType of inner Bundle in Chisel3"
+
+ it should "clone a doubly-nested inner bundle successfully" in {
+ elaborate {
+ class Outer(val w: Int) extends Module {
+ class Middle(val w: Int) {
+ class InnerIOType extends Bundle {
+ val in = Input(UInt(w.W))
+ }
+ def getIO = new InnerIOType
+ }
+ val io = IO((new Middle(w)).getIO)
+ }
+ new Outer(2)
+ }
+ }
+
+ it should "clone an anonymous inner bundle successfully" in {
+ elaborate {
+ class TestTop(val w: Int) extends Module {
+ val io = IO(new Bundle{ val a = UInt(w.W) })
+ }
+ new TestTop(2)
+ }
+ }
+
+ it should "pick the correct $outer instance for an anonymous inner bundle" in {
+ elaborate {
+ class Inner(val w: Int) extends Module {
+ val io = IO(new Bundle{
+ val in = Input(UInt(w.W))
+ val out = Output(UInt(w.W))
+ })
+ }
+ class Outer(val w: Int) extends Module {
+ val io = IO(new Bundle{
+ val in = Input(UInt(w.W))
+ val out = Output(UInt(w.W))
+ })
+ val i = Module(new Inner(w))
+ val iw = Wire(chiselTypeOf(i.io))
+ iw <> io
+ i.io <> iw
+ }
+ new Outer(2)
+ }
+ }
+
+ behavior of "anonymous doubly-nested inner bundle fails with clear error"
+ ( the[ChiselException] thrownBy {
+ elaborate {
+ class Outer(val w: Int) extends Module {
+ class Middle(val w: Int) {
+ def getIO = new Bundle {
+ val in = Input(UInt(w.W))
+ }
+ }
+ val io = IO((new Middle(w)).getIO)
+ }
+ new Outer(2)
+ }
+ }).getMessage should include("non-trivial inner Bundle class")
+
+}