summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/src/main/scala/chisel3/Aggregate.scala56
-rw-r--r--core/src/main/scala/chisel3/internal/Builder.scala8
l---------no-plugin-tests/src/test/scala/chiselTests/AutoClonetypeSpec.scala1
l---------no-plugin-tests/src/test/scala/chiselTests/AutoNestedCloneSpec.scala1
-rw-r--r--no-plugin-tests/src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala (renamed from src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala)0
-rw-r--r--plugin/src/main/scala-2.12/chisel3/internal/plugin/BundleComponent.scala125
-rw-r--r--plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselComponent.scala7
-rw-r--r--plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselPlugin.scala6
-rw-r--r--src/test/scala/chisel3/testers/TestUtils.scala12
-rw-r--r--src/test/scala/chiselTests/AutoClonetypeSpec.scala179
-rw-r--r--src/test/scala/chiselTests/AutoNestedCloneSpec.scala73
11 files changed, 388 insertions, 80 deletions
diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala
index 6942313e..30276230 100644
--- a/core/src/main/scala/chisel3/Aggregate.scala
+++ b/core/src/main/scala/chisel3/Aggregate.scala
@@ -798,6 +798,13 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record {
case _ => None
}
+ /** Indicates if a concrete Bundle class was compiled using the compiler plugin
+ *
+ * Used for optimizing Chisel's performance and testing Chisel itself
+ * @note This should not be used in user code!
+ */
+ protected def _usingPlugin: Boolean = false
+
// Memoize the outer instance for autoclonetype, especially where this is context-dependent
// (like the outer module or enclosing Bundles).
private var _outerInst: Option[Object] = None
@@ -808,7 +815,33 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record {
private val _containingModule: Option[BaseModule] = Builder.currentModule
private val _containingBundles: Seq[Bundle] = Builder.updateBundleStack(this)
- override def cloneType : this.type = {
+ private def checkClone(clone: Bundle): Unit = {
+ for ((name, field) <- elements) {
+ if (clone.elements(name) eq field) {
+ throw new AutoClonetypeException(
+ s"Automatically cloned $clone has field '$name' aliased with base $this." +
+ " In the future, this will be solved automatically by the compiler plugin." +
+ " For now, ensure Chisel types used in the Bundle definition are passed through constructor arguments," +
+ " or wrapped in Input(...), Output(...), or Flipped(...) if appropriate." +
+ " As a last resort, you can override cloneType manually."
+ )
+ }
+ }
+
+ }
+
+ override def cloneType: this.type = {
+ val clone = _cloneTypeImpl.asInstanceOf[this.type]
+ checkClone(clone)
+ clone
+ }
+
+ /** Implementation of cloneType using runtime reflection. This should _never_ be overridden or called in user-code
+ *
+ * @note This is overridden by the compiler plugin (it is never called when using the plugin)
+ */
+ protected def _cloneTypeImpl: Bundle = {
+ assert(Builder.allowReflectiveAutoCloneType, "reflective autoclonetype is disallowed, this should only happen in testing")
// This attempts to infer constructor and arguments to clone this Bundle subtype without
// requiring the user explicitly overriding cloneType.
import scala.language.existentials
@@ -816,24 +849,19 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record {
val clazz = this.getClass
- def autoClonetypeError(desc: String): Nothing = {
- throw new AutoClonetypeException(s"Unable to automatically infer cloneType on $clazz: $desc")
- }
+ def autoClonetypeError(desc: String): Nothing =
+ throw new AutoClonetypeException(
+ s"Unable to automatically infer cloneType on $clazz. " +
+ "cloneType is now implemented by the Chisel compiler plugin so please ensure you are using it in your build. " +
+ "If you cannot use the compiler plugin or you are using it and you still see this message, please file an issue and let us know. " +
+ s"For those not using the plugin, here is the 'runtime reflection' cloneType error message: $desc"
+ )
def validateClone(clone: Bundle, equivDiagnostic: String): Unit = {
if (!clone.typeEquivalent(this)) {
autoClonetypeError(s"Automatically cloned $clone not type-equivalent to base $this. " + equivDiagnostic)
}
-
- for ((name, field) <- elements) {
- if (clone.elements(name) eq field) {
- autoClonetypeError(s"Automatically cloned $clone has field $name aliased with base $this." +
- " In the future, this can be solved by wrapping the field in Field(...)," +
- " see https://github.com/freechipsproject/chisel3/pull/909." +
- " For now, ensure Chisel types used in the Bundle definition are passed through constructor arguments," +
- " or wrapped in Input(...), Output(...), or Flipped(...) if appropriate.")
- }
- }
+ checkClone(clone)
}
val mirror = runtimeMirror(clazz.getClassLoader)
diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala
index e95384cd..b1016a2e 100644
--- a/core/src/main/scala/chisel3/internal/Builder.scala
+++ b/core/src/main/scala/chisel3/internal/Builder.scala
@@ -311,6 +311,8 @@ private[chisel3] class DynamicContext(val annotationSeq: AnnotationSeq) {
val components = ArrayBuffer[Component]()
val annotations = ArrayBuffer[ChiselAnnotation]()
var currentModule: Option[BaseModule] = None
+ // This is only used for testing, it can be removed if the plugin becomes mandatory
+ var allowReflectiveAutoCloneType = true
/** Contains a mapping from a elaborated module to their aspect
* Set by [[ModuleAspect]]
@@ -530,6 +532,12 @@ private[chisel3] object Builder extends LazyLogging {
dynamicContext.currentReset = newReset
}
+ // This should only be used for testing
+ def allowReflectiveAutoCloneType: Boolean = dynamicContext.allowReflectiveAutoCloneType
+ def allowReflectiveAutoCloneType_=(value: Boolean): Unit = {
+ dynamicContext.allowReflectiveAutoCloneType = value
+ }
+
def forcedClock: Clock = currentClock.getOrElse(
throwException("Error: No implicit clock.")
)
diff --git a/no-plugin-tests/src/test/scala/chiselTests/AutoClonetypeSpec.scala b/no-plugin-tests/src/test/scala/chiselTests/AutoClonetypeSpec.scala
new file mode 120000
index 00000000..ae3a7597
--- /dev/null
+++ b/no-plugin-tests/src/test/scala/chiselTests/AutoClonetypeSpec.scala
@@ -0,0 +1 @@
+../../../../../src/test/scala/chiselTests/AutoClonetypeSpec.scala \ No newline at end of file
diff --git a/no-plugin-tests/src/test/scala/chiselTests/AutoNestedCloneSpec.scala b/no-plugin-tests/src/test/scala/chiselTests/AutoNestedCloneSpec.scala
new file mode 120000
index 00000000..4ff3aa4f
--- /dev/null
+++ b/no-plugin-tests/src/test/scala/chiselTests/AutoNestedCloneSpec.scala
@@ -0,0 +1 @@
+../../../../../src/test/scala/chiselTests/AutoNestedCloneSpec.scala \ No newline at end of file
diff --git a/src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala b/no-plugin-tests/src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala
index 28673495..28673495 100644
--- a/src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala
+++ b/no-plugin-tests/src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala
diff --git a/plugin/src/main/scala-2.12/chisel3/internal/plugin/BundleComponent.scala b/plugin/src/main/scala-2.12/chisel3/internal/plugin/BundleComponent.scala
new file mode 100644
index 00000000..0711e08c
--- /dev/null
+++ b/plugin/src/main/scala-2.12/chisel3/internal/plugin/BundleComponent.scala
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.internal.plugin
+
+import scala.collection.mutable
+import scala.tools.nsc
+import scala.tools.nsc.{Global, Phase}
+import scala.tools.nsc.plugins.PluginComponent
+import scala.tools.nsc.symtab.Flags
+import scala.tools.nsc.transform.TypingTransformers
+
+// TODO This component could also implement val elements in Bundles
+private[plugin] class BundleComponent(val global: Global) extends PluginComponent with TypingTransformers {
+ import global._
+
+ val phaseName: String = "chiselbundlephase"
+ val runsAfter: List[String] = "typer" :: Nil
+ def newPhase(prev: Phase): Phase = new BundlePhase(prev)
+
+ private class BundlePhase(prev: Phase) extends StdPhase(prev) {
+ override def name: String = phaseName
+ def apply(unit: CompilationUnit): Unit = {
+ // This plugin doesn't work on Scala 2.11 nor Scala 3. Rather than complicate the sbt build flow,
+ // instead we just check the version and if its an early Scala version, the plugin does nothing
+ val scalaVersion = scala.util.Properties.versionNumberString.split('.')
+ if (scalaVersion(0).toInt == 2 && scalaVersion(1).toInt >= 12) {
+ unit.body = new MyTypingTransformer(unit).transform(unit.body)
+ }
+ }
+ }
+
+ private class MyTypingTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {
+
+ def inferType(t: Tree): Type = localTyper.typed(t, nsc.Mode.TYPEmode).tpe
+
+ val bundleTpe = inferType(tq"chisel3.Bundle")
+ val dataTpe = inferType(tq"chisel3.Data")
+
+ // Not cached because it should only be run once per class (thus once per Type)
+ def isBundle(sym: Symbol): Boolean = sym.tpe <:< bundleTpe
+
+ val isDataCache = new mutable.HashMap[Type, Boolean]
+ // Cached because this is run on every argument to every Bundle
+ def isData(sym: Symbol): Boolean = isDataCache.getOrElseUpdate(sym.tpe, sym.tpe <:< dataTpe)
+
+ def cloneTypeFull(tree: Tree): Tree =
+ localTyper.typed(q"chisel3.experimental.DataMirror.internal.chiselTypeClone[${tree.tpe}]($tree)")
+
+ def isNullaryMethodNamed(name: String, defdef: DefDef): Boolean =
+ defdef.name.decodedName.toString == name && defdef.tparams.isEmpty && defdef.vparamss.isEmpty
+
+ def getConstructorAndParams(body: List[Tree]): (Option[DefDef], Seq[Symbol]) = {
+ val paramAccessors = mutable.ListBuffer[Symbol]()
+ var primaryConstructor: Option[DefDef] = None
+ body.foreach {
+ case acc: ValDef if acc.symbol.isParamAccessor =>
+ paramAccessors += acc.symbol
+ case con: DefDef if con.symbol.isPrimaryConstructor =>
+ primaryConstructor = Some(con)
+ case d: DefDef if isNullaryMethodNamed("_cloneTypeImpl", d) =>
+ val msg = "Users cannot override _cloneTypeImpl. Let the compiler plugin generate it. If you must, override cloneType instead."
+ global.globalError(d.pos, msg)
+ case d: DefDef if isNullaryMethodNamed("_usingPlugin", d) =>
+ val msg = "Users cannot override _usingPlugin, it is for the compiler plugin's use only."
+ global.globalError(d.pos, msg)
+ case _ =>
+ }
+ (primaryConstructor, paramAccessors.toList)
+ }
+
+
+ override def transform(tree: Tree): Tree = tree match {
+
+ case bundle: ClassDef if isBundle(bundle.symbol) && !bundle.mods.hasFlag(Flag.ABSTRACT) =>
+
+ // ==================== Generate _cloneTypeImpl ====================
+ val (con, params) = getConstructorAndParams(bundle.impl.body)
+ if (con.isEmpty) {
+ global.reporter.warning(bundle.pos, "Unable to determine primary constructor!")
+ return super.transform(tree)
+ }
+ val constructor = con.get
+
+ val thiz = gen.mkAttributedThis(bundle.symbol)
+
+ // The params have spaces after them (Scalac implementation detail)
+ val paramLookup: String => Symbol = params.map(sym => sym.name.toString.trim -> sym).toMap
+
+ // Create a this.<ref> for each field matching order of constructor arguments
+ // List of Lists because we can have multiple parameter lists
+ val conArgs: List[List[Tree]] =
+ constructor.vparamss.map(_.map { vp =>
+ val p = paramLookup(vp.name.toString)
+ // Make this.<ref>
+ val select = gen.mkAttributedSelect(thiz, p)
+ // Clone any Data parameters to avoid field aliasing, need full clone to include direction
+ if (isData(vp.symbol)) cloneTypeFull(select) else select
+ })
+
+ val ttpe = Ident(bundle.symbol)
+ val neww = localTyper.typed(New(ttpe, conArgs))
+
+ // Create the symbol for the method and have it be associated with the Bundle class
+ val cloneTypeSym = bundle.symbol.newMethod(TermName("_cloneTypeImpl"), bundle.symbol.pos.focus, Flag.OVERRIDE | Flag.PROTECTED)
+ // Handwritten cloneTypes don't have the Method flag set, unclear if it matters
+ cloneTypeSym.resetFlag(Flags.METHOD)
+ // Need to set the type to chisel3.Bundle for the override to work
+ cloneTypeSym.setInfo(NullaryMethodType(bundleTpe))
+
+ val cloneTypeImpl = localTyper.typed(DefDef(cloneTypeSym, neww))
+
+ // ==================== Generate _usingPlugin ====================
+ // Unclear why quasiquotes work here but didn't for cloneTypeSym, maybe they could.
+ val usingPlugin = localTyper.typed(q"override protected def _usingPlugin: Boolean = true")
+
+ val withMethods = deriveClassDef(bundle) { t =>
+ deriveTemplate(t)(_ :+ cloneTypeImpl :+ usingPlugin)
+ }
+
+ super.transform(localTyper.typed(withMethods))
+
+ case _ => super.transform(tree)
+ }
+ }
+}
diff --git a/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselComponent.scala b/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselComponent.scala
index f86b8322..b1302ba3 100644
--- a/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselComponent.scala
+++ b/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselComponent.scala
@@ -19,9 +19,10 @@ class ChiselComponent(val global: Global) extends PluginComponent with TypingTra
class ChiselComponentPhase(prev: Phase) extends StdPhase(prev) {
override def name: String = phaseName
def apply(unit: CompilationUnit): Unit = {
- // This plugin doesn't work on Scala 2.11. Rather than complicate the sbt build flow,
+ // This plugin doesn't work on Scala 2.11 nor Scala 3. Rather than complicate the sbt build flow,
// instead we just check the version and if its an early Scala version, the plugin does nothing
- if(scala.util.Properties.versionNumberString.split('.')(1).toInt >= 12) {
+ val scalaVersion = scala.util.Properties.versionNumberString.split('.')
+ if (scalaVersion(0).toInt == 2 && scalaVersion(1).toInt >= 12) {
unit.body = new MyTypingTransformer(unit).transform(unit.body)
}
}
@@ -211,4 +212,4 @@ class ChiselComponent(val global: Global) extends PluginComponent with TypingTra
case _ => super.transform(tree)
}
}
-} \ No newline at end of file
+}
diff --git a/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselPlugin.scala b/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselPlugin.scala
index f8a1752c..d5eb21cc 100644
--- a/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselPlugin.scala
+++ b/plugin/src/main/scala-2.12/chisel3/internal/plugin/ChiselPlugin.scala
@@ -10,7 +10,9 @@ import nsc.plugins.{Plugin, PluginComponent}
class ChiselPlugin(val global: Global) extends Plugin {
val name = "chiselplugin"
val description = "Plugin for Chisel 3 Hardware Description Language"
- val components: List[PluginComponent] = List[PluginComponent](new ChiselComponent(global))
+ val components: List[PluginComponent] = List[PluginComponent](
+ new ChiselComponent(global),
+ new BundleComponent(global)
+ )
}
-
diff --git a/src/test/scala/chisel3/testers/TestUtils.scala b/src/test/scala/chisel3/testers/TestUtils.scala
index 12712bf7..c72c779a 100644
--- a/src/test/scala/chisel3/testers/TestUtils.scala
+++ b/src/test/scala/chisel3/testers/TestUtils.scala
@@ -3,10 +3,22 @@
package chisel3.testers
import TesterDriver.Backend
+import chisel3.{Bundle, RawModule}
+import chisel3.internal.firrtl.Circuit
+import chisel3.stage.ChiselStage
import firrtl.AnnotationSeq
object TestUtils {
// Useful because TesterDriver.Backend is chisel3 package private
def containsBackend(annos: AnnotationSeq): Boolean =
annos.collectFirst { case b: Backend => b }.isDefined
+
+ // Allows us to check that the compiler plugin cloneType is actually working
+ val usingPlugin: Boolean = (new Bundle { def check = _usingPlugin }).check
+ def elaborateNoReflectiveAutoCloneType(f: => RawModule): Circuit = {
+ ChiselStage.elaborate {
+ chisel3.internal.Builder.allowReflectiveAutoCloneType = !usingPlugin
+ f
+ }
+ }
}
diff --git a/src/test/scala/chiselTests/AutoClonetypeSpec.scala b/src/test/scala/chiselTests/AutoClonetypeSpec.scala
index b791297d..a3da109a 100644
--- a/src/test/scala/chiselTests/AutoClonetypeSpec.scala
+++ b/src/test/scala/chiselTests/AutoClonetypeSpec.scala
@@ -3,7 +3,7 @@
package chiselTests
import chisel3._
-import chisel3.stage.ChiselStage
+import chisel3.testers.TestUtils
class BundleWithIntArg(val i: Int) extends Bundle {
val out = UInt(i.W)
@@ -65,10 +65,14 @@ class NestedAnonymousBundle extends Bundle {
// Not necessarily good style (and not necessarily recommended), but allowed to preserve compatibility.
class BundleWithArgumentField(val x: Data, val y: Data) extends Bundle
+// TODO all `.suggestNames` are due to https://github.com/chipsalliance/chisel3/issues/1802
class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
+ val usingPlugin: Boolean = TestUtils.usingPlugin
+ val elaborate = TestUtils.elaborateNoReflectiveAutoCloneType _
+
"Bundles with Scala args" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(new Bundle{})
+ elaborate { new Module {
+ val io = IO(new Bundle{}).suggestName("io")
val myWire = Wire(new BundleWithIntArg(8))
assert(myWire.i == 8)
@@ -76,8 +80,8 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
}
"Bundles with Scala implicit args" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(new Bundle{})
+ elaborate { new Module {
+ val io = IO(new Bundle{}).suggestName("io")
implicit val implicitInt: Int = 4
val myWire = Wire(new BundleWithImplicit())
@@ -87,8 +91,8 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
}
"Bundles with Scala explicit and impicit args" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(new Bundle{})
+ elaborate { new Module {
+ val io = IO(new Bundle{}).suggestName("io")
implicit val implicitInt: Int = 4
val myWire = Wire(new BundleWithArgAndImplicit(8))
@@ -99,16 +103,16 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
}
"Subtyped Bundles" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(new Bundle{})
+ elaborate { new Module {
+ val io = IO(new Bundle{}).suggestName("io")
val myWire = Wire(new SubBundle(8, 4))
assert(myWire.i == 8)
assert(myWire.i2 == 4)
} }
- ChiselStage.elaborate { new Module {
- val io = IO(new Bundle{})
+ elaborate { new Module {
+ val io = IO(new Bundle{}).suggestName("io")
val myWire = Wire(new SubBundleVal(8, 4))
@@ -117,68 +121,84 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
} }
}
- "Subtyped Bundles that don't clone well" should "be caught" in {
- a [ChiselException] should be thrownBy extractCause[ChiselException] {
- ChiselStage.elaborate { new Module {
- val io = IO(new Bundle{})
- val myWire = Wire(new SubBundleInvalid(8, 4))
- } }
+ def checkSubBundleInvalid() = {
+ elaborate { new Module {
+ val io = IO(new Bundle{}).suggestName("io")
+ val myWire = Wire(new SubBundleInvalid(8, 4))
+ } }
+ }
+ if (usingPlugin) {
+ "Subtyped Bundles that don't clone well" should "be now be supported!" in {
+ checkSubBundleInvalid()
+ }
+ } else {
+ "Subtyped Bundles that don't clone well" should "be caught" in {
+ a [ChiselException] should be thrownBy extractCause[ChiselException] {
+ checkSubBundleInvalid()
+ }
}
}
"Inner bundles with Scala args" should "not need clonetype" in {
- ChiselStage.elaborate { new ModuleWithInner }
+ elaborate { new ModuleWithInner }
}
"Bundles with arguments as fields" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(Output(new BundleWithArgumentField(UInt(8.W), UInt(8.W))))
+ elaborate { new Module {
+ val io = IO(Output(new BundleWithArgumentField(UInt(8.W), UInt(8.W)))).suggestName("io")
io.x := 1.U
io.y := 1.U
} }
}
+ it should "also work when giving directions to the fields" in {
+ elaborate { new Module {
+ val io = IO(new BundleWithArgumentField(Input(UInt(8.W)), Output(UInt(8.W)))).suggestName("io")
+ io.y := io.x
+ } }
+ }
+
"Bundles inside companion objects" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(Output(new CompanionObjectWithBundle.Inner))
+ elaborate { new Module {
+ val io = IO(Output(new CompanionObjectWithBundle.Inner)).suggestName("io")
io.data := 1.U
} }
}
"Parameterized bundles inside companion objects" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(Output(new CompanionObjectWithBundle.ParameterizedInner(8)))
+ elaborate { new Module {
+ val io = IO(Output(new CompanionObjectWithBundle.ParameterizedInner(8))).suggestName("io")
io.data := 1.U
} }
}
"Nested directioned anonymous Bundles" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(new NestedAnonymousBundle)
+ elaborate { new Module {
+ val io = IO(new NestedAnonymousBundle).suggestName("io")
val a = WireDefault(io)
io.a.a := 1.U
} }
}
"3.0 null compatibility" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
+ elaborate { new Module {
class InnerClassThing {
def createBundle: Bundle = new Bundle {
val a = Output(UInt(8.W))
}
}
- val io = IO((new InnerClassThing).createBundle)
+ val io = IO((new InnerClassThing).createBundle).suggestName("io")
val a = WireDefault(io)
} }
}
"Aliased fields" should "be caught" in {
a [ChiselException] should be thrownBy extractCause[ChiselException] {
- ChiselStage.elaborate { new Module {
+ elaborate { new Module {
val bundleFieldType = UInt(8.W)
val io = IO(Output(new Bundle {
val a = bundleFieldType
- }))
+ })).suggestName("io")
io.a := 0.U
} }
}
@@ -190,8 +210,8 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
val a = typeTuple._1
}
- ChiselStage.elaborate { new Module {
- val io = IO(Output(new BadBundle(UInt(8.W), 1)))
+ elaborate { new Module {
+ val io = IO(Output(new BadBundle(UInt(8.W), 1))).suggestName("io")
io.a := 0.U
} }
}
@@ -204,7 +224,7 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
val x = Output(UInt(3.W))
}))
}
- ChiselStage.elaborate { new TestModule }
+ elaborate { new TestModule }
}
"Wrapped IO construction with parent references" should "not fail for autoclonetype" in {
@@ -216,6 +236,97 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
val x = Output(UInt(blah.W))
}))
}
- ChiselStage.elaborate { new TestModule(3) }
+ elaborate { new TestModule(3) }
+ }
+
+ "Autoclonetype" should "support Bundles with if-blocks" in {
+ class MyModule(n: Int) extends MultiIOModule {
+ val io = IO(new Bundle {
+ val in = Input(UInt(8.W))
+ val out = Output(UInt(8.W))
+ if (n > 4) {
+ println("Here we are!")
+ }
+ })
+ io.out := io.in
+ }
+ elaborate(new MyModule(3))
+ }
+
+ // New tests from the plugin
+ if (usingPlugin) {
+ behavior of "Compiler Plugin Autoclonetype"
+
+ it should "support Bundles with non-val parameters" in {
+ class MyBundle(i: Int) extends Bundle {
+ val foo = UInt(i.W)
+ }
+ elaborate { new MultiIOModule {
+ val in = IO(Input(new MyBundle(8)))
+ val out = IO(Output(new MyBundle(8)))
+ out := in
+ }}
+ }
+
+ it should "support type-parameterized Bundles" in {
+ class MyBundle[T <: Data](gen: T) extends Bundle {
+ val foo = gen
+ }
+ elaborate { new MultiIOModule {
+ val in = IO(Input(new MyBundle(UInt(8.W))))
+ val out = IO(Output(new MyBundle(UInt(8.W))))
+ out := in
+ }}
+ }
+
+ it should "support Bundles with non-val implicit parameters" in {
+ class MyBundle(implicit i: Int) extends Bundle {
+ val foo = UInt(i.W)
+ }
+ elaborate { new MultiIOModule {
+ implicit val x = 8
+ val in = IO(Input(new MyBundle))
+ val out = IO(Output(new MyBundle))
+ out := in
+ }}
+ }
+
+ it should "support Bundles with multiple parameter lists" in {
+ class MyBundle(i: Int)(j: Int, jj: Int)(k: UInt) extends Bundle {
+ val foo = UInt((i + j + jj + k.getWidth).W)
+ }
+ elaborate {
+ new MultiIOModule {
+ val in = IO(Input(new MyBundle(8)(8, 8)(UInt(8.W))))
+ val out = IO(Output(new MyBundle(8)(8, 8)(UInt(8.W))))
+ out := in
+ }
+ }
+ }
+
+ it should "support Bundles that implement their own cloneType" in {
+ class MyBundle(i: Int) extends Bundle {
+ val foo = UInt(i.W)
+ override def cloneType = new MyBundle(i).asInstanceOf[this.type]
+ }
+ elaborate { new MultiIOModule {
+ val in = IO(Input(new MyBundle(8)))
+ val out = IO(Output(new MyBundle(8)))
+ out := in
+ }}
+ }
+
+ it should "support Bundles that capture type parameters from their parent scope" in {
+ class MyModule[T <: Data](gen: T) extends MultiIOModule {
+ class MyBundle(n: Int) extends Bundle {
+ val foo = Vec(n, gen)
+ }
+ val in = IO(Input(new MyBundle(4)))
+ val out = IO(Output(new MyBundle(4)))
+ out := in
+ }
+ elaborate(new MyModule(UInt(8.W)))
+ }
+
}
}
diff --git a/src/test/scala/chiselTests/AutoNestedCloneSpec.scala b/src/test/scala/chiselTests/AutoNestedCloneSpec.scala
index 8e40ad20..401766e2 100644
--- a/src/test/scala/chiselTests/AutoNestedCloneSpec.scala
+++ b/src/test/scala/chiselTests/AutoNestedCloneSpec.scala
@@ -1,10 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
package chiselTests
-import Chisel.ChiselException
-import org.scalatest._
import chisel3._
-import chisel3.stage.ChiselStage
+import chisel3.testers.TestUtils
import org.scalatest.matchers.should.Matchers
class BundleWithAnonymousInner(val w: Int) extends Bundle {
@@ -13,11 +11,15 @@ class BundleWithAnonymousInner(val w: Int) extends Bundle {
}
}
+// TODO all `.suggestNames` are due to https://github.com/chipsalliance/chisel3/issues/1802
class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
+ val usingPlugin: Boolean = TestUtils.usingPlugin
+ val elaborate = TestUtils.elaborateNoReflectiveAutoCloneType _
+
behavior of "autoCloneType of inner Bundle in Chisel3"
it should "clone a doubly-nested inner bundle successfully" in {
- ChiselStage.elaborate {
+ elaborate {
class Outer(val w: Int) extends Module {
class Middle(val w: Int) {
class InnerIOType extends Bundle {
@@ -25,7 +27,7 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
}
def getIO: InnerIOType = new InnerIOType
}
- val io = IO(new Bundle {})
+ val io = IO(new Bundle {}).suggestName("io")
val myWire = Wire((new Middle(w)).getIO)
}
new Outer(2)
@@ -33,9 +35,9 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
}
it should "clone an anonymous inner bundle successfully" in {
- ChiselStage.elaborate {
+ elaborate {
class TestTop(val w: Int) extends Module {
- val io = IO(new Bundle {})
+ val io = IO(new Bundle {}).suggestName("io")
val myWire = Wire(new Bundle{ val a = UInt(w.W) })
}
new TestTop(2)
@@ -43,18 +45,18 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
}
it should "pick the correct $outer instance for an anonymous inner bundle" in {
- ChiselStage.elaborate {
+ 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))
- })
+ }).suggestName("io")
}
class Outer(val w: Int) extends Module {
val io = IO(new Bundle{
val in = Input(UInt(w.W))
val out = Output(UInt(w.W))
- })
+ }).suggestName("io")
val i = Module(new Inner(w))
val iw = Wire(chiselTypeOf(i.io))
iw <> io
@@ -65,9 +67,9 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
}
it should "clone an anonymous, bound, inner bundle of another bundle successfully" in {
- ChiselStage.elaborate {
+ elaborate {
class TestModule(w: Int) extends Module {
- val io = IO(new BundleWithAnonymousInner(w) )
+ val io = IO(new BundleWithAnonymousInner(w) ).suggestName("io")
val w0 = WireDefault(io)
val w1 = WireDefault(io.inner)
}
@@ -76,14 +78,14 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
}
it should "clone an anonymous, inner bundle of a Module, bound to another bundle successfully" in {
- ChiselStage.elaborate {
+ elaborate {
class TestModule(w: Int) extends Module {
val bun = new Bundle {
val foo = UInt(w.W)
}
val io = IO(new Bundle {
val inner = Input(bun)
- })
+ }).suggestName("io")
val w0 = WireDefault(io)
val w1 = WireDefault(io.inner)
}
@@ -92,31 +94,48 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
}
it should "clone a double-nested anonymous Bundle" in {
- ChiselStage.elaborate {
+ elaborate {
class TestModule() extends Module {
val io = IO(new Bundle {
val inner = Input(new Bundle {
val x = UInt(8.W)
})
- })
+ }).suggestName("io")
}
new TestModule()
}
}
- // Test ignored because the compatibility null-inserting autoclonetype doesn't trip this
- ignore should "fail on anonymous doubly-nested inner bundle with clear error" in {
- intercept[ChiselException] { extractCause[ChiselException] { ChiselStage.elaborate {
- class Outer(val w: Int) extends Module {
- class Middle(val w: Int) {
- def getIO: Bundle = new Bundle {
- val in = Input(UInt(w.W))
+ if (usingPlugin) {
+ // This works with the plugin, but is a null pointer exception when using reflective autoclonetype
+ it should "support an anonymous doubly-nested inner bundle" in {
+ elaborate {
+ class Outer(val w: Int) extends Module {
+ class Middle(val w: Int) {
+ def getIO: Bundle = new Bundle {
+ val in = Input(UInt(w.W))
+ }
}
+ val io = IO(new Bundle {}).suggestName("io")
+ val myWire = Wire((new Middle(w)).getIO)
}
- val io = IO(new Bundle {})
- val myWire = Wire((new Middle(w)).getIO)
+ new Outer(2)
}
- new Outer(2)
- }}}.getMessage should include("Unable to determine instance")
+ }
+
+ it should "support anonymous Inner bundles that capture type parameters from outer Bundles" in {
+ elaborate(new MultiIOModule {
+ class MyBundle[T <: Data](n: Int, gen: T) extends Bundle {
+ val foo = new Bundle {
+ val x = Input(Vec(n, gen))
+ }
+ val bar = Output(Option(new { def mkBundle = new Bundle { val x = Vec(n, gen) }}).get.mkBundle)
+ }
+ val io = IO(new MyBundle(4, UInt(8.W)))
+ val myWire = WireInit(io.foo)
+ val myWire2 = WireInit(io.bar)
+ io.bar.x := io.foo.x
+ })
+ }
}
}