summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.sbt51
-rw-r--r--core/src/main/scala/chisel3/Data.scala20
-rw-r--r--core/src/main/scala/chisel3/Module.scala4
-rw-r--r--core/src/main/scala/chisel3/RawModule.scala23
-rw-r--r--core/src/main/scala/chisel3/SeqUtils.scala10
-rw-r--r--core/src/main/scala/chisel3/experimental/package.scala14
-rw-r--r--core/src/main/scala/chisel3/internal/Builder.scala194
-rw-r--r--core/src/main/scala/chisel3/internal/Namer.scala6
-rw-r--r--core/src/main/scala/chisel3/internal/firrtl/IR.scala4
-rw-r--r--core/src/main/scala/chisel3/internal/prefix.scala84
-rw-r--r--macros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala4
-rw-r--r--plugin/src/main/resources/scalac-plugin.xml4
-rw-r--r--plugin/src/main/scala-2.12/chisel3/plugin/ChiselPlugin.scala139
-rw-r--r--src/main/scala/chisel3/util/TransitName.scala8
-rw-r--r--src/test/scala/chiselTests/BetterNamingTests.scala12
-rw-r--r--src/test/scala/chiselTests/ChiselSpec.scala25
-rw-r--r--src/test/scala/chiselTests/naming/NamePluginSpec.scala207
-rw-r--r--src/test/scala/chiselTests/naming/PrefixSpec.scala348
18 files changed, 1111 insertions, 46 deletions
diff --git a/build.sbt b/build.sbt
index ea0c0a15..74406b2b 100644
--- a/build.sbt
+++ b/build.sbt
@@ -125,6 +125,54 @@ lazy val chiselSettings = Seq (
}
)
+autoCompilerPlugins := true
+
+// Plugin must be fully cross-versioned (published for Scala minor version)
+// The plugin only works in Scala 2.12+
+lazy val pluginScalaVersions = Seq(
+ "2.11.12", // Only to support chisel3 cross building for 2.11, plugin does nothing in 2.11
+ // scalamacros paradise version used is not published for 2.12.0 and 2.12.1
+ "2.12.2",
+ "2.12.3",
+ "2.12.4",
+ "2.12.5",
+ "2.12.6",
+ "2.12.7",
+ "2.12.8",
+ "2.12.9",
+ "2.12.10",
+ "2.12.11"
+)
+
+lazy val plugin = (project in file("plugin")).
+ settings(name := "chisel3-plugin").
+ settings(commonSettings: _*).
+ settings(publishSettings: _*).
+ settings(
+ libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value,
+ scalacOptions += "-Xfatal-warnings",
+ crossScalaVersions := pluginScalaVersions,
+ // Must be published for Scala minor version
+ crossVersion := CrossVersion.full,
+ crossTarget := {
+ // workaround for https://github.com/sbt/sbt/issues/5097
+ target.value / s"scala-${scalaVersion.value}"
+ },
+ // Only publish for Scala 2.12
+ publish / skip := !scalaVersion.value.startsWith("2.12")
+ )
+
+lazy val usePluginSettings = Seq(
+ scalacOptions in Compile ++= {
+ val jar = (plugin / Compile / Keys.`package`).value
+ val addPlugin = "-Xplugin:" + jar.getAbsolutePath
+ // add plugin timestamp to compiler options to trigger recompile of
+ // main after editing the plugin. (Otherwise a 'clean' is needed.)
+ val dummy = "-Jdummy=" + jar.lastModified
+ Seq(addPlugin, dummy)
+ }
+)
+
lazy val macros = (project in file("macros")).
settings(name := "chisel3-macros").
settings(commonSettings: _*).
@@ -162,9 +210,10 @@ lazy val chisel = (project in file(".")).
settings(commonSettings: _*).
settings(chiselSettings: _*).
settings(publishSettings: _*).
+ settings(usePluginSettings: _*).
dependsOn(macros).
dependsOn(core).
- aggregate(macros, core).
+ aggregate(macros, core, plugin).
settings(
scalacOptions in Test ++= Seq("-language:reflectiveCalls"),
scalacOptions in Compile in doc ++= Seq(
diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala
index 46c98bae..983307a6 100644
--- a/core/src/main/scala/chisel3/Data.scala
+++ b/core/src/main/scala/chisel3/Data.scala
@@ -283,6 +283,14 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
}
}
+ override def autoSeed(name: String): this.type = {
+ topBindingOpt match {
+ // Ports are special in that the autoSeed will keep the first name, not the last name
+ case Some(PortBinding(m)) if hasAutoSeed && Builder.currentModule.contains(m) => this
+ case _ => super.autoSeed(name)
+ }
+ }
+
// 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
@@ -490,7 +498,11 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
* @param that the $coll to connect to
* @group Connect
*/
- final def := (that: Data)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Unit = this.connect(that)(sourceInfo, connectionCompileOptions)
+ final def := (that: => Data)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Unit = {
+ prefix(this) {
+ this.connect(that)(sourceInfo, connectionCompileOptions)
+ }
+ }
/** Connect this $coll to that $coll bi-directionally and element-wise.
*
@@ -499,7 +511,11 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc {
* @param that the $coll to connect to
* @group Connect
*/
- final def <> (that: Data)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Unit = this.bulkConnect(that)(sourceInfo, connectionCompileOptions)
+ final def <> (that: => Data)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Unit = {
+ prefix(this) {
+ this.bulkConnect(that)(sourceInfo, connectionCompileOptions)
+ }
+ }
@chiselRuntimeDeprecated
@deprecated("litArg is deprecated, use litOption or litTo*Option", "3.2")
diff --git a/core/src/main/scala/chisel3/Module.scala b/core/src/main/scala/chisel3/Module.scala
index 140e3003..25037b00 100644
--- a/core/src/main/scala/chisel3/Module.scala
+++ b/core/src/main/scala/chisel3/Module.scala
@@ -41,6 +41,8 @@ object Module extends SourceInfoDoc {
// Save then clear clock and reset to prevent leaking scope, must be set again in the Module
val (saveClock, saveReset) = (Builder.currentClock, Builder.currentReset)
+ val savePrefix = Builder.getPrefix()
+ Builder.clearPrefix()
Builder.currentClock = None
Builder.currentReset = None
@@ -67,6 +69,8 @@ object Module extends SourceInfoDoc {
val component = module.generateComponent()
Builder.components += component
+ Builder.setPrefix(savePrefix)
+
// Handle connections at enclosing scope
if(!Builder.currentModule.isEmpty) {
pushCommand(DefInstance(sourceInfo, module, component.ports))
diff --git a/core/src/main/scala/chisel3/RawModule.scala b/core/src/main/scala/chisel3/RawModule.scala
index 0fcec266..5b609384 100644
--- a/core/src/main/scala/chisel3/RawModule.scala
+++ b/core/src/main/scala/chisel3/RawModule.scala
@@ -42,7 +42,7 @@ abstract class RawModule(implicit moduleCompileOptions: CompileOptions)
private[chisel3] def namePorts(names: HashMap[HasId, String]): Unit = {
for (port <- getModulePorts) {
- port.suggestedName.orElse(names.get(port)) match {
+ port.computeName(None, None).orElse(names.get(port)) match {
case Some(name) =>
if (_namespace.contains(name)) {
Builder.error(s"""Unable to name port $port to "$name" in $this,""" +
@@ -75,13 +75,21 @@ abstract class RawModule(implicit moduleCompileOptions: CompileOptions)
// All suggestions are in, force names to every node.
for (id <- getIds) {
id match {
- case id: BaseModule => id.forceName(default=id.desiredName, _namespace)
- case id: MemBase[_] => id.forceName(default="_T", _namespace)
+ case id: BaseModule => id.forceName(None, default=id.desiredName, _namespace)
+ case id: MemBase[_] => id.forceName(None, default="MEM", _namespace)
case id: Data =>
if (id.isSynthesizable) {
id.topBinding match {
- case OpBinding(_) | MemoryPortBinding(_) | PortBinding(_) | RegBinding(_) | WireBinding(_) =>
- id.forceName(default="_T", _namespace)
+ case OpBinding(_) =>
+ id.forceName(Some(""), default="T", _namespace)
+ case MemoryPortBinding(_) =>
+ id.forceName(None, default="MPORT", _namespace)
+ case PortBinding(_) =>
+ id.forceName(None, default="PORT", _namespace)
+ case RegBinding(_) =>
+ id.forceName(None, default="REG", _namespace)
+ case WireBinding(_) =>
+ id.forceName(Some(""), default="WIRE", _namespace)
case _ => // don't name literals
}
} // else, don't name unbound types
@@ -152,8 +160,8 @@ trait RequireSyncReset extends MultiIOModule {
abstract class MultiIOModule(implicit moduleCompileOptions: CompileOptions)
extends RawModule {
// Implicit clock and reset pins
- final val clock: Clock = IO(Input(Clock()))
- final val reset: Reset = IO(Input(mkReset))
+ final val clock: Clock = IO(Input(Clock())).autoSeed("clock")
+ final val reset: Reset = IO(Input(mkReset)).autoSeed("reset")
private[chisel3] def mkReset: Reset = {
// Top module and compatibility mode use Bool for reset
@@ -164,6 +172,7 @@ abstract class MultiIOModule(implicit moduleCompileOptions: CompileOptions)
// Setup ClockAndReset
Builder.currentClock = Some(clock)
Builder.currentReset = Some(reset)
+ Builder.clearPrefix()
private[chisel3] override def initializeInParent(parentCompileOptions: CompileOptions): Unit = {
implicit val sourceInfo = UnlocatableSourceInfo
diff --git a/core/src/main/scala/chisel3/SeqUtils.scala b/core/src/main/scala/chisel3/SeqUtils.scala
index 9f09b2c2..9f068898 100644
--- a/core/src/main/scala/chisel3/SeqUtils.scala
+++ b/core/src/main/scala/chisel3/SeqUtils.scala
@@ -3,7 +3,7 @@
package chisel3
import chisel3.experimental.FixedPoint
-import chisel3.internal.throwException
+import chisel3.internal.{prefix, throwException}
import scala.language.experimental.macros
import chisel3.internal.sourceinfo._
@@ -23,8 +23,12 @@ private[chisel3] object SeqUtils {
if (in.tail.isEmpty) {
in.head.asUInt
} else {
- val left = asUInt(in.slice(0, in.length/2))
- val right = asUInt(in.slice(in.length/2, in.length))
+ val left = prefix("left") {
+ asUInt(in.slice(0, in.length/2))
+ }.autoSeed("left")
+ val right = prefix("right") {
+ asUInt(in.slice(in.length/2, in.length))
+ }.autoSeed("right")
right ## left
}
}
diff --git a/core/src/main/scala/chisel3/experimental/package.scala b/core/src/main/scala/chisel3/experimental/package.scala
index da103318..71fd186c 100644
--- a/core/src/main/scala/chisel3/experimental/package.scala
+++ b/core/src/main/scala/chisel3/experimental/package.scala
@@ -136,4 +136,18 @@ package object experimental {
}
}
}
+
+ // Use to add a prefix to any component generated in input scope
+ val prefix = chisel3.internal.prefix
+ // Use to remove prefixes not in provided scope
+ val noPrefix = chisel3.internal.noPrefix
+ // Used by Chisel's compiler plugin to automatically name signals
+ def autoNameRecursively[T <: Any](name: String, nameMe: T): T = {
+ chisel3.internal.Builder.nameRecursively(
+ name.replace(" ", ""),
+ nameMe,
+ (id: chisel3.internal.HasId, n: String) => id.autoSeed(n)
+ )
+ nameMe
+ }
}
diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala
index 510919bc..ceb14900 100644
--- a/core/src/main/scala/chisel3/internal/Builder.scala
+++ b/core/src/main/scala/chisel3/internal/Builder.scala
@@ -9,6 +9,7 @@ import chisel3.experimental._
import chisel3.internal.firrtl._
import chisel3.internal.naming._
import _root_.firrtl.annotations.{CircuitName, ComponentName, IsMember, ModuleName, Named, ReferenceTarget}
+import chisel3.internal.Builder.Prefix
import scala.collection.mutable
@@ -88,25 +89,133 @@ private[chisel3] trait HasId extends InstanceId {
override def hashCode: Int = super.hashCode()
override def equals(that: Any): Boolean = super.equals(that)
- // Facilities for 'suggesting' a name to this.
- // Post-name hooks called to carry the suggestion to other candidates as needed
- private var suggested_name: Option[String] = None
- private val postname_hooks = scala.collection.mutable.ListBuffer.empty[String=>Unit]
- // Only takes the first suggestion!
- def suggestName(name: =>String): this.type = {
- if(suggested_name.isEmpty) suggested_name = Some(name)
- for(hook <- postname_hooks) { hook(name) }
+ // Contains suggested seed (user-decided seed)
+ private var suggested_seed: Option[String] = None
+
+ // Contains the seed computed automatically by the compiler plugin
+ private var auto_seed: Option[String] = None
+
+ // Prefix at time when this class is constructed
+ private val construction_prefix: Prefix = Builder.getPrefix()
+
+ // Prefix when the latest [[suggestSeed]] or [[autoSeed]] is called
+ private var prefix_seed: Prefix = List.empty[Either[String, Data]]
+
+ // Post-seed hooks called to carry the suggested seeds to other candidates as needed
+ private val suggest_postseed_hooks = scala.collection.mutable.ListBuffer.empty[String=>Unit]
+
+ // Post-seed hooks called to carry the auto seeds to other candidates as needed
+ private val auto_postseed_hooks = scala.collection.mutable.ListBuffer.empty[String=>Unit]
+
+ /** Takes the last seed suggested. Multiple calls to this function will take the last given seed, unless
+ * this HasId is a module port (see overridden method in Data.scala).
+ *
+ * If the final computed name conflicts with the final name of another signal, the final name may get uniquified by
+ * appending a digit at the end of the name.
+ *
+ * Is a lower priority than [[suggestName]], in that regardless of whether [[autoSeed]]
+ * was called, [[suggestName]] will always take precedence if it was called.
+ *
+ * @param seed Seed for the name of this component
+ * @return this object
+ */
+ def autoSeed(seed: String): this.type = {
+ auto_seed = Some(seed)
+ for(hook <- auto_postseed_hooks) { hook(seed) }
+ prefix_seed = Builder.getPrefix()
this
}
- private[chisel3] def suggestedName: Option[String] = suggested_name
- private[chisel3] def addPostnameHook(hook: String=>Unit): Unit = postname_hooks += hook
+
+ /** Takes the first seed suggested. Multiple calls to this function will be ignored.
+ * If the final computed name conflicts with another name, it may get uniquified by appending
+ * a digit at the end.
+ *
+ * Is a higher priority than [[autoSeed]], in that regardless of whether [[autoSeed]]
+ * was called, [[suggestName]] will always take precedence.
+ *
+ * @param seed The seed for the name of this component
+ * @return this object
+ */
+ def suggestName(seed: =>String): this.type = {
+ if(suggested_seed.isEmpty) suggested_seed = Some(seed)
+ prefix_seed = Builder.getPrefix()
+ for(hook <- suggest_postseed_hooks) { hook(seed) }
+ this
+ }
+
+ /** Computes the name of this HasId, if one exists
+ * @param defaultPrefix Optionally provide a default prefix for computing the name
+ * @param defaultSeed Optionally provide default seed for computing the name
+ * @return the name, if it can be computed
+ */
+ def computeName(defaultPrefix: Option[String], defaultSeed: Option[String]): Option[String] = {
+ // Recursively builds a name if referenced fields of an aggregate type
+ def buildAggName(id: HasId): Option[String] = {
+ def recArg(node: Arg): Option[String] = node match {
+ case Slot(imm, name) => recArg(imm).map(_ + "_" + name)
+ case Index(imm, ILit(num)) => recArg(imm).map(_ + "_" + num)
+ case Index(imm, n: LitArg) => recArg(imm).map(_ + "_" + n.num)
+ case Index(imm, _: Node) => recArg(imm)
+ case Node(id) => recArg(id.getOptionRef.get)
+ case Ref(name) => Some(name)
+ case ModuleIO(mod, name) if _parent.contains(mod) => Some(name)
+ case ModuleIO(mod, name) => recArg(mod.getRef).map(_ + "_" + name)
+ }
+ id.getOptionRef.flatMap(recArg)
+ }
+
+ /** Computes a name of this signal, given the seed and prefix
+ * @param seed
+ * @param prefix
+ * @return
+ */
+ def buildName(seed: String, prefix: Prefix): String = {
+ val builder = new StringBuilder()
+ prefix.foreach {
+ case Left(s: String) => builder ++= s + "_"
+ case Right(d: HasId) =>
+ buildAggName(d) match {
+ case Some(n) => builder ++= n + "_"
+ case _ =>
+ }
+ case _ =>
+ }
+ builder ++= seed
+ builder.toString
+ }
+
+ if(hasSeed) {
+ Some(buildName(seedOpt.get, prefix_seed))
+ } else {
+ defaultSeed.map { default =>
+ defaultPrefix match {
+ case Some(p) => buildName(default, Left(p) +: construction_prefix)
+ case None => buildName(default, construction_prefix)
+ }
+ }
+ }
+ }
+
+ /** This resolves the precedence of [[autoSeed]] and [[suggestName]]
+ *
+ * @return the current calculation of a name, if it exists
+ */
+ private[chisel3] def seedOpt: Option[String] = suggested_seed.orElse(auto_seed)
+
+ /** @return Whether either autoName or suggestName has been called */
+ def hasSeed: Boolean = seedOpt.isDefined
+
+ private[chisel3] def hasAutoSeed: Boolean = auto_seed.isDefined
+
+ private[chisel3] def addSuggestPostnameHook(hook: String=>Unit): Unit = suggest_postseed_hooks += hook
+ private[chisel3] def addAutoPostnameHook(hook: String=>Unit): Unit = auto_postseed_hooks += hook
// Uses a namespace to convert suggestion into a true name
// Will not do any naming if the reference already assigned.
// (e.g. tried to suggest a name to part of a Record)
- private[chisel3] def forceName(default: =>String, namespace: Namespace): Unit =
+ private[chisel3] def forceName(prefix: Option[String], default: =>String, namespace: Namespace): Unit =
if(_ref.isEmpty) {
- val candidate_name = suggested_name.getOrElse(default)
+ val candidate_name = computeName(prefix, Some(default)).get
val available_name = namespace.name(candidate_name)
setRef(Ref(available_name))
}
@@ -124,7 +233,7 @@ private[chisel3] trait HasId extends InstanceId {
case Some(p) => p._component match {
case Some(c) => _ref match {
case Some(arg) => arg fullName c
- case None => suggested_name.getOrElse("??")
+ case None => computeName(None, None).get
}
case None => throwException("signalName/pathName should be called after circuit elaboration")
}
@@ -158,9 +267,14 @@ private[chisel3] trait HasId extends InstanceId {
}
}
val valNames = getValNames(this.getClass)
- def isPublicVal(m: java.lang.reflect.Method) =
- m.getParameterTypes.isEmpty && valNames.contains(m.getName) && !m.getDeclaringClass.isAssignableFrom(rootClass)
- this.getClass.getMethods.sortWith(_.getName < _.getName).filter(isPublicVal(_))
+ def isPublicVal(m: java.lang.reflect.Method) = {
+ val noParameters = m.getParameterTypes.isEmpty
+ val aVal = valNames.contains(m.getName)
+ val notAssignable = !m.getDeclaringClass.isAssignableFrom(rootClass)
+ val notWeirdVal = !m.getName.contains('$')
+ noParameters && aVal && notAssignable && notWeirdVal
+ }
+ this.getClass.getMethods.filter(isPublicVal).sortWith(_.getName < _.getName)
}
}
/** Holds the implementation of toNamed for Data and MemBase */
@@ -199,6 +313,9 @@ private[chisel3] class ChiselContext() {
// Record the Bundle instance, class name, method name, and reverse stack trace position of open Bundles
val bundleStack: ArrayBuffer[(Bundle, String, String, Int)] = ArrayBuffer()
+
+ // Records the different prefixes which have been scoped at this point in time
+ val prefixStack: ArrayBuffer[Either[String, HasId]] = ArrayBuffer()
}
private[chisel3] class DynamicContext() {
@@ -223,6 +340,10 @@ private[chisel3] class DynamicContext() {
}
private[chisel3] object Builder {
+
+ // Represents the current state of the prefixes given
+ type Prefix = List[Either[String, Data]]
+
// All global mutable state must be referenced via dynamicContextVar!!
private val dynamicContextVar = new DynamicVariable[Option[DynamicContext]](None)
private def dynamicContext: DynamicContext = {
@@ -258,6 +379,43 @@ private[chisel3] object Builder {
def annotations: ArrayBuffer[ChiselAnnotation] = dynamicContext.annotations
def namingStack: NamingStack = dynamicContext.namingStack
+ // Puts either a prefix string or hasId onto the prefix stack
+ def pushPrefix(d: Either[String, HasId]): Unit = {
+ chiselContext.get().prefixStack += d
+ }
+
+ // Puts a prefix string onto the prefix stack
+ def pushPrefix(d: String): Unit = {
+ chiselContext.get().prefixStack += Left(d)
+ }
+
+ // Puts a prefix data onto the prefix stack
+ def pushPrefix(d: HasId): Unit = {
+ chiselContext.get().prefixStack += Right(d)
+ }
+
+ // Remove a prefix from top of the stack
+ def popPrefix(): Either[String, HasId] = {
+ val ps = chiselContext.get().prefixStack
+ ps.remove(ps.size - 1)
+ }
+
+ // Removes all prefixes from the prefix stack
+ def clearPrefix(): Unit = {
+ val ps = chiselContext.get().prefixStack
+ ps.clear()
+ }
+
+ // Clears existing prefixes and sets to new prefix stack
+ def setPrefix(prefix: Prefix): Unit = {
+ val ps = chiselContext.get().prefixStack
+ clearPrefix()
+ ps.insertAll(0, prefix)
+ }
+
+ // Returns the prefix stack at this moment
+ def getPrefix(): Prefix = chiselContext.get().prefixStack.toList.asInstanceOf[Prefix]
+
def currentModule: Option[BaseModule] = dynamicContextVar.value match {
case Some(dyanmicContext) => dynamicContext.currentModule
case _ => None
@@ -411,7 +569,7 @@ private[chisel3] object Builder {
dynamicContextVar.withValue(Some(new DynamicContext())) {
errors.info("Elaborating design...")
val mod = f
- mod.forceName(mod.name, globalNamespace)
+ mod.forceName(None, mod.name, globalNamespace)
errors.checkpoint()
errors.info("Done elaborating.")
@@ -446,7 +604,7 @@ object DynamicNamingStack {
}
prefixRef
}
-
+
def length() : Int = Builder.namingStackOption.get.length
}
diff --git a/core/src/main/scala/chisel3/internal/Namer.scala b/core/src/main/scala/chisel3/internal/Namer.scala
index 999971a4..0153c0df 100644
--- a/core/src/main/scala/chisel3/internal/Namer.scala
+++ b/core/src/main/scala/chisel3/internal/Namer.scala
@@ -72,7 +72,7 @@ class NamingContext extends NamingContextInterface {
val descendants = new IdentityHashMap[AnyRef, ListBuffer[NamingContext]]()
val anonymousDescendants = ListBuffer[NamingContext]()
val items = ListBuffer[(AnyRef, String)]()
- var closed = false // a sanity check to ensure no more name() calls are done after name_prefix
+ var closed = false // a sanity check to ensure no more name() calls are done after namePrefix
/** Adds a NamingContext object as a descendant - where its contained objects will have names
* prefixed with the name given to the reference object, if the reference object is named in the
@@ -87,7 +87,7 @@ class NamingContext extends NamingContextInterface {
}
def name[T](obj: T, name: String): T = {
- assert(!closed, "Can't name elements after name_prefix called")
+ assert(!closed, "Can't name elements after namePrefix called")
obj match {
case _: NoChiselNamePrefix => // Don't name things with NoChiselNamePrefix
case ref: AnyRef => items += ((ref, name))
@@ -149,6 +149,6 @@ class NamingStack {
namingStack.top.addDescendant(prefixRef, until)
}
}
-
+
def length() : Int = namingStack.length
}
diff --git a/core/src/main/scala/chisel3/internal/firrtl/IR.scala b/core/src/main/scala/chisel3/internal/firrtl/IR.scala
index 44fdb833..1783f68f 100644
--- a/core/src/main/scala/chisel3/internal/firrtl/IR.scala
+++ b/core/src/main/scala/chisel3/internal/firrtl/IR.scala
@@ -72,11 +72,11 @@ abstract class Arg {
case class Node(id: HasId) extends Arg {
override def fullName(ctx: Component): String = id.getOptionRef match {
case Some(arg) => arg.fullName(ctx)
- case None => id.suggestedName.getOrElse("??")
+ case None => id.instanceName
}
def name: String = id.getOptionRef match {
case Some(arg) => arg.name
- case None => id.suggestedName.getOrElse("??")
+ case None => id.instanceName
}
}
diff --git a/core/src/main/scala/chisel3/internal/prefix.scala b/core/src/main/scala/chisel3/internal/prefix.scala
new file mode 100644
index 00000000..fbb1318c
--- /dev/null
+++ b/core/src/main/scala/chisel3/internal/prefix.scala
@@ -0,0 +1,84 @@
+// See LICENSE for license details.
+
+package chisel3.internal
+
+/** Use to add a prefix to any components generated in the provided scope.
+ *
+ * @example {{{
+ *
+ * val x1 = prefix("first") {
+ * // Anything generated here will be prefixed with "first"
+ * }
+ *
+ * val x2 = prefix(mysignal) {
+ * // Anything generated here will be prefixed with the name of mysignal
+ * }
+ *
+ * }}}
+ *
+ */
+private[chisel3] object prefix { // scalastyle:ignore
+
+ /** Use to add a prefix to any components generated in the provided scope
+ * The prefix is the name of the provided which, which may not be known yet.
+ *
+ * @param name The signal/instance whose name will be the prefix
+ * @param f a function for which any generated components are given the prefix
+ * @tparam T The return type of the provided function
+ * @return The return value of the provided function
+ */
+ def apply[T](name: HasId)(f: => T): T = {
+ Builder.pushPrefix(name)
+ val ret = f
+ Builder.popPrefix()
+ ret
+ }
+
+ /** Use to add a prefix to any components generated in the provided scope
+ * The prefix is a string, which must be known when this function is used.
+ *
+ * @param name The name which will be the prefix
+ * @param f a function for which any generated components are given the prefix
+ * @tparam T The return type of the provided function
+ * @return The return value of the provided function
+ */
+ def apply[T](name: String)(f: => T): T = {
+ Builder.pushPrefix(name)
+ val ret = f
+ // Sometimes val's can occur between the Module.apply and Module constructor
+ // This causes extra prefixes to be added, and subsequently cleared in the
+ // Module constructor. Thus, we need to just make sure if the previous push
+ // was an incorrect one, to not pop off an empty stack
+ if(Builder.getPrefix().nonEmpty) Builder.popPrefix()
+ ret
+ }
+}
+
+/** Use to eliminate any existing prefixes within the provided scope.
+ *
+ * @example {{{
+ *
+ * val x1 = noPrefix {
+ * // Anything generated here will not be prefixed by anything outside this scope
+ * }
+ *
+ * }}}
+ *
+ */
+private[chisel3] object noPrefix {
+
+ /** Use to clear existing prefixes so no signals within the scope are prefixed by signals/names
+ * outside the scope
+ *
+ * @param f a function for which any generated components are given the prefix
+ * @tparam T The return type of the provided function
+ * @return The return value of the provided function
+ */
+ def apply[T](f: => T): T = {
+ val prefix = Builder.getPrefix()
+ Builder.clearPrefix()
+ val ret = f
+ Builder.setPrefix(prefix)
+ ret
+ }
+}
diff --git a/macros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala b/macros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala
index 88baa2b1..d8f2ff40 100644
--- a/macros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala
+++ b/macros/src/main/scala/chisel3/internal/naming/NamingAnnotations.scala
@@ -112,7 +112,7 @@ class NamingTransforms(val c: Context) {
}
}
- /** Applies the val name transform to a class body.
+ /** Applies the val name transform to a class body.
* Closes context on top level or return local context to englobing context.
* Closing context only makes sense when top level a Module.
* A Module is always the naming top level.
@@ -128,7 +128,7 @@ class NamingTransforms(val c: Context) {
if($globalNamingStack.length == 1){
$contextVar.namePrefix("")
}
- $globalNamingStack.popReturnContext(this, $contextVar)
+ $globalNamingStack.popReturnContext(this, $contextVar)
"""
}
diff --git a/plugin/src/main/resources/scalac-plugin.xml b/plugin/src/main/resources/scalac-plugin.xml
new file mode 100644
index 00000000..b2b44fc6
--- /dev/null
+++ b/plugin/src/main/resources/scalac-plugin.xml
@@ -0,0 +1,4 @@
+<plugin>
+ <name>chiselplugin</name>
+ <classname>chisel3.plugin.ChiselPlugin</classname>
+</plugin>
diff --git a/plugin/src/main/scala-2.12/chisel3/plugin/ChiselPlugin.scala b/plugin/src/main/scala-2.12/chisel3/plugin/ChiselPlugin.scala
new file mode 100644
index 00000000..785a253d
--- /dev/null
+++ b/plugin/src/main/scala-2.12/chisel3/plugin/ChiselPlugin.scala
@@ -0,0 +1,139 @@
+// See LICENSE for license details.
+
+package chisel3.plugin
+
+import scala.tools.nsc
+import nsc.{Global, Phase}
+import nsc.plugins.Plugin
+import nsc.plugins.PluginComponent
+import scala.reflect.internal.Flags
+import scala.tools.nsc.transform.TypingTransformers
+
+// The plugin to be run by the Scala compiler during compilation of Chisel code
+class ChiselPlugin(val global: Global) extends Plugin {
+ val name = "chiselplugin"
+ val description = "Plugin for Chisel 3 Hardware Description Language"
+ val components = List[PluginComponent](new ChiselComponent(global))
+}
+
+// The component of the chisel plugin. Not sure exactly what the difference is between
+// a Plugin and a PluginComponent.
+class ChiselComponent(val global: Global) extends PluginComponent with TypingTransformers {
+ import global._
+ val runsAfter = List[String]("typer")
+ override val runsRightAfter: Option[String] = Some("typer")
+ val phaseName: String = "chiselcomponent"
+ def newPhase(_prev: Phase): ChiselComponentPhase = new ChiselComponentPhase(_prev)
+ 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,
+ // 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) {
+ unit.body = new MyTypingTransformer(unit).transform(unit.body)
+ }
+ }
+ }
+
+ class MyTypingTransformer(unit: CompilationUnit)
+ extends TypingTransformer(unit) {
+
+ // Determines if the chisel plugin should match on this type
+ def shouldMatch(q: Type, bases: Seq[Tree]): Boolean = {
+
+ // If subtype of Data or BaseModule, its a match!
+ def terminate(t: Type): Boolean = bases.exists { base => t <:< inferType(base) }
+
+ // Recurse through subtype hierarchy finding containers
+ // Seen is only updated when we recurse into type parameters, thus it is typically small
+ def recShouldMatch(s: Type, seen: Set[Type]): Boolean = {
+ def outerMatches(t: Type): Boolean = {
+ val str = t.toString
+ str.startsWith("Option[") || str.startsWith("Iterable[")
+ }
+ if (terminate(s)) {
+ true
+ } else if (seen.contains(s)) {
+ false
+ } else if (outerMatches(s)) {
+ // These are type parameters, loops *are* possible here
+ recShouldMatch(s.typeArgs.head, seen + s)
+ } else {
+ // This is the standard inheritance hierarchy, Scalac catches loops here
+ s.parents.exists( p => recShouldMatch(p, seen) )
+ }
+ }
+
+ // If doesn't match container pattern, exit early
+ def earlyExit(t: Type): Boolean = {
+ !(t.matchesPattern(inferType(tq"Iterable[_]")) || t.matchesPattern(inferType(tq"Option[_]")))
+ }
+
+ // First check if a match, then check early exit, then recurse
+ if(terminate(q)){
+ true
+ } else if(earlyExit(q)) {
+ false
+ } else {
+ recShouldMatch(q, Set.empty)
+ }
+ }
+
+ // Given a type tree, infer the type and return it
+ def inferType(t: Tree): Type = localTyper.typed(t, nsc.Mode.TYPEmode).tpe
+
+ // Indicates whether a ValDef is properly formed to get name
+ def okVal(dd: ValDef, bases: Tree*): Boolean = {
+
+ // These were found through trial and error
+ def okFlags(mods: Modifiers): Boolean = {
+ val badFlags = Set(
+ Flag.PARAM,
+ Flag.SYNTHETIC,
+ Flag.DEFERRED,
+ Flags.TRIEDCOOKING,
+ Flags.CASEACCESSOR,
+ Flags.PARAMACCESSOR
+ )
+ badFlags.forall{ x => !mods.hasFlag(x)}
+ }
+
+ // Ensure expression isn't null, as you can't call `null.autoName("myname")`
+ val isNull = dd.rhs match {
+ case Literal(Constant(null)) => true
+ case _ => false
+ }
+ okFlags(dd.mods) && shouldMatch(inferType(dd.tpt), bases) && !isNull && dd.rhs != EmptyTree
+ }
+
+ // Whether this val is directly enclosed by a Bundle type
+ def inBundle(dd: ValDef): Boolean = {
+ dd.symbol.logicallyEnclosingMember.thisType <:< inferType(tq"chisel3.Bundle")
+ }
+
+ // Method called by the compiler to modify source tree
+ override def transform(tree: Tree): Tree = tree match {
+ // If a Data and in a Bundle, just get the name but not a prefix
+ case dd @ ValDef(mods, name, tpt, rhs) if okVal(dd, tq"chisel3.Data") && inBundle(dd) =>
+ val TermName(str: String) = name
+ val newRHS = super.transform(rhs)
+ val named = q"chisel3.experimental.autoNameRecursively($str, $newRHS)"
+ treeCopy.ValDef(dd, mods, name, tpt, localTyper typed named)
+ // If a Data or a Memory, get the name and a prefix
+ case dd @ ValDef(mods, name, tpt, rhs) if okVal(dd, tq"chisel3.Data", tq"chisel3.MemBase[_]") =>
+ val TermName(str: String) = name
+ val newRHS = super.transform(rhs)
+ val prefixed = q"chisel3.experimental.prefix.apply[$tpt](name=$str)(f=$newRHS)"
+ val named = q"chisel3.experimental.autoNameRecursively($str, $prefixed)"
+ treeCopy.ValDef(dd, mods, name, tpt, localTyper typed named)
+ // If an instance, just get a name but no prefix
+ case dd @ ValDef(mods, name, tpt, rhs) if okVal(dd, tq"chisel3.experimental.BaseModule") =>
+ val TermName(str: String) = name
+ val newRHS = super.transform(rhs)
+ val named = q"chisel3.experimental.autoNameRecursively($str, $newRHS)"
+ treeCopy.ValDef(dd, mods, name, tpt, localTyper typed named)
+ // Otherwise, continue
+ case _ => super.transform(tree)
+ }
+ }
+}
diff --git a/src/main/scala/chisel3/util/TransitName.scala b/src/main/scala/chisel3/util/TransitName.scala
index 3b9a3869..992f48d2 100644
--- a/src/main/scala/chisel3/util/TransitName.scala
+++ b/src/main/scala/chisel3/util/TransitName.scala
@@ -43,7 +43,9 @@ object TransitName {
* @return the `from` parameter
*/
def apply[T<:HasId](from: T, to: HasId): T = {
- from.addPostnameHook((given_name: String) => {to.suggestName(given_name)})
+ // To transit a name, we need to hook on both the suggestName and autoSeed mechanisms
+ from.addSuggestPostnameHook((given_name: String) => {to.suggestName(given_name)})
+ from.addAutoPostnameHook((given_name: String) => {to.autoSeed(given_name)})
from
}
@@ -55,7 +57,9 @@ object TransitName {
* @return the `from` parameter
*/
def withSuffix[T<:HasId](suffix: String)(from: T, to: HasId): T = {
- from.addPostnameHook((given_name: String) => {to.suggestName(given_name + suffix)})
+ // To transit a name, we need to hook on both the suggestName and autoSeed mechanisms
+ from.addSuggestPostnameHook((given_name: String) => {to.suggestName(given_name + suffix)})
+ from.addAutoPostnameHook((given_name: String) => {to.autoSeed(given_name + suffix)})
from
}
diff --git a/src/test/scala/chiselTests/BetterNamingTests.scala b/src/test/scala/chiselTests/BetterNamingTests.scala
index 032b634e..dd17a015 100644
--- a/src/test/scala/chiselTests/BetterNamingTests.scala
+++ b/src/test/scala/chiselTests/BetterNamingTests.scala
@@ -36,12 +36,14 @@ class IterableNaming extends NamedModuleTester {
expectName(WireDefault(2.U), "optSet_2"),
expectName(WireDefault(3.U), "optSet_3")))
- val stack = mutable.Stack[Module]()
- for (i <- 0 until 4) {
- val j = 3 - i
- stack.push(expectName(Module(new Other(i)), s"stack_$j"))
+ val stack = {
+ val s = mutable.Stack[Module]()
+ for (i <- 0 until 4) {
+ val j = 3 - i
+ s.push(expectName(Module(new Other(i)), s"stack_$j"))
+ }
+ s
}
-
def streamFrom(x: Int): Stream[Module] =
expectName(Module(new Other(x)), s"list_$x") #:: streamFrom(x + 1)
val stream = streamFrom(0) // Check that we don't get into infinite loop
diff --git a/src/test/scala/chiselTests/ChiselSpec.scala b/src/test/scala/chiselTests/ChiselSpec.scala
index 2ee6fbcf..7980e772 100644
--- a/src/test/scala/chiselTests/ChiselSpec.scala
+++ b/src/test/scala/chiselTests/ChiselSpec.scala
@@ -14,6 +14,9 @@ import firrtl.annotations.DeletedAnnotation
import firrtl.util.BackendCompilationUtilities
import java.io.ByteArrayOutputStream
import java.security.Permission
+
+import chisel3.aop.Aspect
+import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage, NoRunFirrtlCompilerAnnotation, PrintFullStackTraceAnnotation}
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
import scala.reflect.ClassTag
@@ -270,6 +273,27 @@ trait Utils {
}
}
+
+ /** A tester which runs generator and uses an aspect to check the returned object
+ * @param gen function to generate a Chisel module
+ * @param f a function to check the Chisel module
+ * @tparam T the Chisel module class
+ */
+ def aspectTest[T <: RawModule](gen: () => T)(f: T => Unit)(implicit scalaMajorVersion: Int): Unit = {
+ // Runs chisel stage
+ def run[T <: RawModule](gen: () => T, annotations: AnnotationSeq): AnnotationSeq = {
+ new ChiselStage().run(Seq(ChiselGeneratorAnnotation(gen), NoRunFirrtlCompilerAnnotation, PrintFullStackTraceAnnotation) ++ annotations)
+ }
+ // Creates a wrapping aspect to contain checking function
+ case object BuiltAspect extends Aspect[T] {
+ override def toAnnotation(top: T): AnnotationSeq = {f(top); Nil}
+ }
+ val currentMajorVersion = scala.util.Properties.versionNumberString.split('.')(1).toInt
+ if(currentMajorVersion >= scalaMajorVersion) {
+ run(gen, Seq(BuiltAspect))
+ }
+ }
+
/** Run some code and rethrow an exception with a specific type if an exception of that type occurs anywhere in the
* stack trace.
*
@@ -306,5 +330,4 @@ trait Utils {
}
}
-
}
diff --git a/src/test/scala/chiselTests/naming/NamePluginSpec.scala b/src/test/scala/chiselTests/naming/NamePluginSpec.scala
new file mode 100644
index 00000000..fc90264d
--- /dev/null
+++ b/src/test/scala/chiselTests/naming/NamePluginSpec.scala
@@ -0,0 +1,207 @@
+// See LICENSE for license details.
+
+package chiselTests.naming
+
+import chisel3._
+import chisel3.aop.Select
+import chisel3.experimental.{prefix, treedump}
+import chiselTests.{ChiselFlatSpec, Utils}
+
+class NamePluginSpec extends ChiselFlatSpec with Utils {
+ implicit val minimumScalaVersion: Int = 12
+
+ "Scala plugin" should "name internally scoped components" in {
+ class Test extends MultiIOModule {
+ { val mywire = Wire(UInt(3.W))}
+ }
+ aspectTest(() => new Test) {
+ top: Test => Select.wires(top).head.toTarget.ref should be("mywire")
+ }
+ }
+
+ "Scala plugin" should "name internally scoped instances" in {
+ class Inner extends MultiIOModule { }
+ class Test extends MultiIOModule {
+ { val myinstance = Module(new Inner) }
+ }
+ aspectTest(() => new Test) {
+ top: Test => Select.instances(top).head.instanceName should be("myinstance")
+ }
+ }
+
+ "Scala plugin" should "interact with prefixing" in {
+ class Test extends MultiIOModule {
+ def builder() = {
+ val wire = Wire(UInt(3.W))
+ }
+ prefix("first") {
+ builder()
+ }
+ prefix("second") {
+ builder()
+ }
+ }
+ aspectTest(() => new Test) {
+ top: Test => Select.wires(top).map(_.instanceName) should be (List("first_wire", "second_wire"))
+ }
+ }
+
+ "Scala plugin" should "interact with prefixing so last val name wins" in {
+ class Test extends MultiIOModule {
+ def builder() = {
+ val wire1 = Wire(UInt(3.W))
+ val wire2 = Wire(UInt(3.W))
+ wire2
+ }
+ {
+ val x1 = prefix("first") {
+ builder()
+ }
+ }
+ {
+ val x2 = prefix("second") {
+ builder()
+ }
+ }
+ }
+ aspectTest(() => new Test) {
+ top: Test => Select.wires(top).map(_.instanceName) should be (List("x1_first_wire1", "x1", "x2_second_wire1", "x2"))
+ }
+ }
+
+ "Naming on option" should "work" in {
+
+ class Test extends MultiIOModule {
+ def builder(): Option[UInt] = {
+ val a = Wire(UInt(3.W))
+ Some(a)
+ }
+
+ { val blah = builder() }
+ }
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.wires(top).map(_.instanceName) should be (List("blah"))
+ }
+ }
+
+
+ "Naming on iterables" should "work" in {
+
+ class Test extends MultiIOModule {
+ def builder(): Seq[UInt] = {
+ val a = Wire(UInt(3.W))
+ val b = Wire(UInt(3.W))
+ Seq(a, b)
+ }
+ {
+ val blah = {
+ builder()
+ }
+ }
+ }
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.wires(top).map(_.instanceName) should be (List("blah_0", "blah_1"))
+ }
+ }
+
+ "Naming on nested iterables" should "work" in {
+
+ class Test extends MultiIOModule {
+ def builder(): Seq[Seq[UInt]] = {
+ val a = Wire(UInt(3.W))
+ val b = Wire(UInt(3.W))
+ val c = Wire(UInt(3.W))
+ val d = Wire(UInt(3.W))
+ Seq(Seq(a, b), Seq(c, d))
+ }
+ {
+ val blah = {
+ builder()
+ }
+ }
+ }
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.wires(top).map(_.instanceName) should be (
+ List(
+ "blah_0_0",
+ "blah_0_1",
+ "blah_1_0",
+ "blah_1_1"
+ ))
+ }
+ }
+
+ "Naming on custom case classes" should "not work" in {
+ case class Container(a: UInt, b: UInt)
+
+ class Test extends MultiIOModule {
+ def builder(): Container = {
+ val a = Wire(UInt(3.W))
+ val b = Wire(UInt(3.W))
+ Container(a, b)
+ }
+
+ { val blah = builder() }
+ }
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.wires(top).map(_.instanceName) should be (List("a", "b"))
+ }
+ }
+
+ "Multiple names on an IO within a module" should "get the first name" in {
+ class Test extends RawModule {
+ {
+ val a = IO(Output(UInt(3.W)))
+ val b = a
+ }
+ }
+
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.ios(top).map(_.instanceName) should be (List("a"))
+ }
+ }
+
+ "Multiple names on a non-IO" should "get the last name" in {
+ class Test extends MultiIOModule {
+ {
+ val a = Wire(UInt(3.W))
+ val b = a
+ }
+ }
+
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.wires(top).map(_.instanceName) should be (List("b"))
+ }
+ }
+
+ "Unapply assignments" should "still be named" in {
+ class Test extends MultiIOModule {
+ {
+ val (a, b) = (Wire(UInt(3.W)), Wire(UInt(3.W)))
+ }
+ }
+
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.wires(top).map(_.instanceName) should be (List("a", "b"))
+ }
+ }
+
+ "Recursive types" should "not infinitely loop" in {
+ // When this fails, it causes a StackOverflow when compiling the tests
+ // Unfortunately, this doesn't seem to work with assertCompiles(...), it probably ignores the
+ // custom project scalacOptions
+ def func(x: String) = {
+ // We only check types of vals, we don't actually want to run this code though
+ val y = scala.xml.XML.loadFile(x)
+ y
+ }
+ }
+}
+
diff --git a/src/test/scala/chiselTests/naming/PrefixSpec.scala b/src/test/scala/chiselTests/naming/PrefixSpec.scala
new file mode 100644
index 00000000..df350829
--- /dev/null
+++ b/src/test/scala/chiselTests/naming/PrefixSpec.scala
@@ -0,0 +1,348 @@
+// See LICENSE for license details.
+
+package chiselTests.naming
+
+import chisel3._
+import chisel3.aop.Select
+import chisel3.experimental.{dump, noPrefix, prefix, treedump}
+import chiselTests.{ChiselPropSpec, Utils}
+
+class PrefixSpec extends ChiselPropSpec with Utils {
+ implicit val minimumMajorVersion: Int = 12
+ property("Scala plugin should interact with prefixing so last plugin name wins?") {
+ class Test extends MultiIOModule {
+ def builder(): UInt = {
+ val wire1 = Wire(UInt(3.W))
+ val wire2 = Wire(UInt(3.W))
+ wire2
+ }
+
+ {
+ val x1 = prefix("first") {
+ builder()
+ }
+ }
+ {
+ val x2 = prefix("second") {
+ builder()
+ }
+ }
+ }
+ aspectTest(() => new Test) {
+ top: Test => Select.wires(top).map(_.instanceName) should be (List("x1_first_wire1", "x1", "x2_second_wire1", "x2"))
+ }
+ }
+
+ property("Nested prefixes should work") {
+ class Test extends MultiIOModule {
+ def builder2(): UInt = {
+ val wire1 = Wire(UInt(3.W))
+ val wire2 = Wire(UInt(3.W))
+ wire2
+ }
+ def builder(): UInt = {
+ val wire1 = Wire(UInt(3.W))
+ val wire2 = Wire(UInt(3.W))
+ prefix("foo") {
+ builder2()
+ }
+ }
+ { val x1 = builder() }
+ { val x2 = builder() }
+ }
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.wires(top).map(_.instanceName) should be (
+ List(
+ "x1_wire1",
+ "x1_wire2",
+ "x1_foo_wire1",
+ "x1",
+ "x2_wire1",
+ "x2_wire2",
+ "x2_foo_wire1",
+ "x2"
+ )
+ )
+ }
+ }
+
+ property("Prefixing seeded with signal") {
+ class Test extends MultiIOModule {
+ @treedump
+ @dump
+ def builder(): UInt = {
+ val wire = Wire(UInt(3.W))
+ wire := 3.U
+ wire
+ }
+ {
+ val x1 = Wire(UInt(3.W))
+ x1 := {
+ builder()
+ }
+ val x2 = Wire(UInt(3.W))
+ x2 := {
+ builder()
+ }
+ }
+ }
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.wires(top).map(_.instanceName) should be (List("x1", "x1_wire", "x2", "x2_wire"))
+ }
+ }
+
+ property("Automatic prefixing should work") {
+
+ class Test extends MultiIOModule {
+ def builder(): UInt = {
+ val a = Wire(UInt(3.W))
+ val b = Wire(UInt(3.W))
+ b
+ }
+
+ {
+ val ADAM = builder()
+ val JACOB = builder()
+ }
+ }
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.wires(top).map(_.instanceName) should be (List("ADAM_a", "ADAM", "JACOB_a", "JACOB"))
+ }
+ }
+
+ property("No prefixing annotation on defs should work") {
+
+ class Test extends MultiIOModule {
+ def builder(): UInt = noPrefix {
+ val a = Wire(UInt(3.W))
+ val b = Wire(UInt(3.W))
+ b
+ }
+
+ { val noprefix = builder() }
+ }
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.wires(top).map(_.instanceName) should be (List("a", "noprefix"))
+ }
+ }
+
+ property("Prefixing on temps should work") {
+
+ class Test extends MultiIOModule {
+ def builder(): UInt = {
+ val a = Wire(UInt(3.W))
+ val b = Wire(UInt(3.W))
+ a +& (b * a)
+ }
+
+ { val blah = builder() }
+ }
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.ops(top).map(x => (x._1, x._2.instanceName)) should be (List(
+ ("mul", "_blah_T"),
+ ("add", "blah")
+ ))
+ }
+ }
+
+ property("Prefixing should not leak into child modules") {
+ class Child extends MultiIOModule {
+ {
+ val wire = Wire(UInt())
+ }
+ }
+
+ class Test extends MultiIOModule {
+ {
+ val child = prefix("InTest") {
+ Module(new Child)
+ }
+ }
+ }
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.wires(Select.instances(top).head).map(_.instanceName) should be (List("wire"))
+ }
+ }
+
+ property("Prefixing should not leak into child modules, example 2") {
+ class Child extends MultiIOModule {
+ {
+ val wire = Wire(UInt())
+ }
+ }
+
+ class Test extends MultiIOModule {
+ val x = IO(Input(UInt(3.W)))
+ val y = {
+ lazy val module = new Child
+ val child = Module(module)
+ }
+ }
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.wires(Select.instances(top).head).map(_.instanceName) should be (List("wire"))
+ }
+ }
+
+ property("Instance names should not be added to prefix") {
+ class Child(tpe: UInt) extends MultiIOModule {
+ {
+ val io = IO(Input(tpe))
+ }
+ }
+
+ class Test extends MultiIOModule {
+ {
+ lazy val module = {
+ val x = UInt(3.W)
+ new Child(x)
+ }
+ val child = Module(module)
+ }
+ }
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.ios(Select.instances(top).head).map(_.instanceName) should be (List("clock", "reset", "io"))
+ }
+ }
+
+
+ property("Prefixing should not be caused by nested Iterable[Iterable[Any]]") {
+ class Test extends MultiIOModule {
+ {
+ val iia = {
+ val wire = Wire(UInt(3.W))
+ List(List("Blah"))
+ }
+ }
+ }
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.wires(top).map(_.instanceName) should be (List("wire"))
+ }
+ }
+
+ property("Prefixing should be caused by nested Iterable[Iterable[Data]]") {
+ class Test extends MultiIOModule {
+ {
+ val iia = {
+ val wire = Wire(UInt(3.W))
+ List(List(3.U))
+ }
+ }
+ }
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.wires(top).map(_.instanceName) should be (List("iia_wire"))
+ }
+ }
+
+ property("Prefixing should be the prefix during the last call to autoName/suggestName") {
+ class Test extends MultiIOModule {
+ {
+ val wire = {
+ val x = Wire(UInt(3.W)).suggestName("mywire")
+ x
+ }
+ }
+ }
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.wires(top).map(_.instanceName) should be (List("mywire"))
+ Select.wires(top).map(_.instanceName) shouldNot be (List("wire_mywire"))
+ }
+ }
+
+ property("Prefixing have intuitive behavior") {
+ class Test extends MultiIOModule {
+ {
+ val wire = {
+ val x = Wire(UInt(3.W)).suggestName("mywire")
+ val y = Wire(UInt(3.W)).suggestName("mywire2")
+ y := x
+ y
+ }
+ }
+ }
+ aspectTest(() => new Test) {
+ top: Test => Select.wires(top).map(_.instanceName) should be (List("wire_mywire", "mywire2"))
+ }
+ }
+
+ property("Prefixing on connection to subfields work") {
+ class Test extends MultiIOModule {
+ {
+ val wire = Wire(new Bundle {
+ val x = UInt(3.W)
+ val y = UInt(3.W)
+ val vec = Vec(4, UInt(3.W))
+ })
+ wire.x := RegNext(3.U)
+ wire.y := RegNext(3.U)
+ wire.vec(0) := RegNext(3.U)
+ wire.vec(wire.x) := RegNext(3.U)
+ }
+ }
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.registers(top).map(_.instanceName) should be (List(
+ "wire_x_REG",
+ "wire_y_REG",
+ "wire_vec_0_REG",
+ "wire_vec_REG"
+ ))
+ }
+ }
+
+ property("Prefixing on connection to IOs should work") {
+ class Child extends MultiIOModule {
+ val in = IO(Input(UInt(3.W)))
+ val out = IO(Output(UInt(3.W)))
+ out := RegNext(in)
+ }
+ class Test extends MultiIOModule {
+ {
+ val child = Module(new Child)
+ child.in := RegNext(3.U)
+ }
+ }
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.registers(top).map(_.instanceName) should be (List(
+ "child_in_REG"
+ ))
+ Select.registers(Select.instances(top).head).map(_.instanceName) should be (List(
+ "out_REG"
+ ))
+ }
+ }
+
+ property("Prefixing on bulk connects should work") {
+ class Child extends MultiIOModule {
+ val in = IO(Input(UInt(3.W)))
+ val out = IO(Output(UInt(3.W)))
+ out := RegNext(in)
+ }
+ class Test extends MultiIOModule {
+ {
+ val child = Module(new Child)
+ child.in <> RegNext(3.U)
+ }
+ }
+ aspectTest(() => new Test) {
+ top: Test =>
+ Select.registers(top).map(_.instanceName) should be (List(
+ "child_in_REG"
+ ))
+ Select.registers(Select.instances(top).head).map(_.instanceName) should be (List(
+ "out_REG"
+ ))
+ }
+ }
+}