summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.sbt13
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/Builder.scala10
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/Namer.scala151
-rw-r--r--coreMacros/src/main/scala/chisel3/internal/sourceinfo/NamingAnnotations.scala180
-rw-r--r--src/main/scala/chisel3/compatibility.scala27
-rw-r--r--src/main/scala/chisel3/package.scala17
-rw-r--r--src/test/scala/chiselTests/NamingAnnotationTest.scala200
7 files changed, 589 insertions, 9 deletions
diff --git a/build.sbt b/build.sbt
index 90004d2d..5fa8895b 100644
--- a/build.sbt
+++ b/build.sbt
@@ -22,6 +22,8 @@ lazy val commonSettings = Seq (
autoAPIMappings := true,
scalaVersion := "2.11.7",
scalacOptions := Seq("-deprecation", "-feature"),
+ libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value,
+ addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full),
// Since we want to examine the classpath to determine if a dependency on firrtl is required,
// this has to be a Task setting.
// Fortunately, allDependencies is a Task Setting, so we can modify that.
@@ -78,7 +80,6 @@ lazy val chiselSettings = Seq (
libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "2.2.5" % "test",
- "org.scala-lang" % "scala-reflect" % scalaVersion.value,
"org.scalacheck" %% "scalacheck" % "1.12.4" % "test",
"com.github.scopt" %% "scopt" % "3.4.0"
),
@@ -96,17 +97,11 @@ lazy val chiselSettings = Seq (
lazy val coreMacros = (project in file("coreMacros")).
settings(commonSettings: _*).
- settings(
- libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value,
- publishArtifact := false
- )
+ settings(publishArtifact := false)
lazy val chiselFrontend = (project in file("chiselFrontend")).
settings(commonSettings: _*).
- settings(
- libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value,
- publishArtifact := false
- ).
+ settings(publishArtifact := false).
dependsOn(coreMacros)
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala
index c93dbfc7..5d841941 100644
--- a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala
+++ b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala
@@ -151,6 +151,7 @@ private[chisel3] class DynamicContext() {
// Used to distinguish between no Module() wrapping, multiple wrappings, and rewrapping
var readyForModuleConstr: Boolean = false
val errors = new ErrorLog
+ val namingStack = new internal.naming.NamingStack
}
private[chisel3] object Builder {
@@ -163,6 +164,7 @@ private[chisel3] object Builder {
def globalNamespace: Namespace = dynamicContext.globalNamespace
def components: ArrayBuffer[Component] = dynamicContext.components
def annotations: ArrayBuffer[ChiselAnnotation] = dynamicContext.annotations
+ def namingStack: internal.naming.NamingStack = dynamicContext.namingStack
def currentModule: Option[Module] = dynamicContext.currentModule
def currentModule_=(target: Option[Module]): Unit = {
@@ -222,3 +224,11 @@ private[chisel3] object Builder {
}
}
}
+
+/** Allows public access to the naming stack in Builder / DynamicContext.
+ * Necessary because naming macros expand in user code and don't have access into private[chisel3]
+ * objects.
+ */
+object DynamicNamingStack {
+ def apply() = Builder.namingStack
+}
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Namer.scala b/chiselFrontend/src/main/scala/chisel3/internal/Namer.scala
new file mode 100644
index 00000000..a7196a22
--- /dev/null
+++ b/chiselFrontend/src/main/scala/chisel3/internal/Namer.scala
@@ -0,0 +1,151 @@
+// See LICENSE for license details.
+
+// This file contains part of the implementation of the naming static annotation system.
+
+package chisel3.internal.naming
+
+import scala.collection.mutable.Stack
+import scala.collection.mutable.ListBuffer
+
+import scala.collection.JavaConversions._
+
+import java.util.IdentityHashMap
+
+/** Recursive Function Namer overview
+ *
+ * In every function, creates a NamingContext object, which associates all vals with a string name
+ * suffix, for example:
+ * val myValName = SomeStatement()
+ * produces the entry in items:
+ * {ref of SomeStatement(), "myValName"}
+ *
+ * This is achieved with a macro transforming:
+ * val myValName = SomeStatement()
+ * statements into a naming call:
+ * val myValName = context.name(SomeStatement(), "myValName")
+ *
+ * The context is created from a global dynamic context stack at the beginning of each function.
+ * At the end of each function call, the completed context is added to its parent context and
+ * associated with the return value (whose name at an enclosing function call will form the prefix
+ * for all named objects).
+ *
+ * When the naming context prefix is given, it will name all of its items with the prefix and the
+ * associated suffix name. Then, it will check its descendants for sub-contexts with references
+ * matching the item reference, and if there is a match, it will (recursively) give the
+ * sub-context a prefix of its current prefix plus the item reference suffix.
+ *
+ * Note that for Modules, the macro will insert a naming context prefix call with an empty prefix,
+ * starting the recursive naming process.
+ */
+
+/** Base class for naming contexts, providing the basic API consisting of naming calls and
+ * ability to take descendant naming contexts.
+ */
+class NamingContext {
+ 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
+
+ /** 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
+ * scope of this context.
+ */
+ def add_descendant(ref: AnyRef, descendant: NamingContext) {
+ if (!descendants.containsKey(ref)) {
+ descendants.put(ref, ListBuffer[NamingContext]())
+ }
+ descendants.get(ref) += descendant
+ }
+
+ def add_anonymous_descendant(descendant: NamingContext) {
+ anonymousDescendants += descendant
+ }
+
+ /** Suggest a name (that will be propagated to FIRRTL) for an object, then returns the object
+ * itself (so this can be inserted transparently anywhere).
+ * Is a no-op (so safe) when applied on objects that aren't named, including non-Chisel data
+ * types.
+ */
+ def name[T](obj: T, name: String): T = {
+ assert(!closed, "Can't name elements after name_prefix called")
+ obj match {
+ case ref: AnyRef => items += ((ref, name))
+ case _ =>
+ }
+ obj
+ }
+
+ /** Gives this context a naming prefix (which may be empty, "", for a top-level Module context)
+ * so that actual naming calls (HasId.suggestName) can happen.
+ * Recursively names descendants, for those whose return value have an associated name.
+ */
+ def name_prefix(prefix: String) {
+ closed = true
+ for ((ref, suffix) <- items) {
+ // First name the top-level object
+ ref match {
+ case nameable: chisel3.internal.HasId => nameable.suggestName(prefix + suffix)
+ case _ =>
+ }
+
+ // Then recurse into descendant contexts
+ if (descendants.containsKey(ref)) {
+ for (descendant <- descendants.get(ref)) {
+ descendant.name_prefix(prefix + suffix + "_")
+ }
+ descendants.remove(ref)
+ }
+ }
+
+ for (descendant <- descendants.values().flatten) {
+ // Where we have a broken naming link, just ignore the missing parts
+ descendant.name_prefix(prefix)
+ }
+ for (descendant <- anonymousDescendants) {
+ descendant.name_prefix(prefix)
+ }
+ }
+}
+
+/** Class for the (global) naming stack object, which provides a way to push and pop naming
+ * contexts as functions are called / finished.
+ */
+class NamingStack {
+ val naming_stack = Stack[NamingContext]()
+
+ /** Creates a new naming context, where all items in the context will have their names prefixed
+ * with some yet-to-be-determined prefix from object names in an enclosing scope.
+ */
+ def push_context(): NamingContext = {
+ val context = new NamingContext
+ naming_stack.push(context)
+ context
+ }
+
+ /** Called at the end of a function, popping the current naming context, adding it to the
+ * enclosing context's descendants, and passing through the prefix naming reference.
+ * Every instance of push_context() must have a matching pop_context().
+ *
+ * Will assert out if the context being popped isn't the topmost on the stack.
+ */
+ def pop_return_context[T <: Any](prefix_ref: T, until: NamingContext): T = {
+ assert(naming_stack.top == until)
+ naming_stack.pop()
+ if (!naming_stack.isEmpty) {
+ prefix_ref match {
+ case prefix_ref: AnyRef => naming_stack.top.add_descendant(prefix_ref, until)
+ case _ => naming_stack.top.add_anonymous_descendant(until)
+ }
+
+ }
+ prefix_ref
+ }
+
+ /** Same as pop_return_context, but for cases where there is no return value (like Module scope).
+ */
+ def pop_context(until: NamingContext) {
+ assert(naming_stack.top == until)
+ naming_stack.pop()
+ }
+}
diff --git a/coreMacros/src/main/scala/chisel3/internal/sourceinfo/NamingAnnotations.scala b/coreMacros/src/main/scala/chisel3/internal/sourceinfo/NamingAnnotations.scala
new file mode 100644
index 00000000..1f7c1cac
--- /dev/null
+++ b/coreMacros/src/main/scala/chisel3/internal/sourceinfo/NamingAnnotations.scala
@@ -0,0 +1,180 @@
+// See LICENSE for license details.
+
+// Transform implementations for name-propagation related annotations.
+//
+// Helpful references:
+// http://docs.scala-lang.org/overviews/quasiquotes/syntax-summary.html#definitions
+// for quasiquote structures of various Scala structures
+// http://jsuereth.com/scala/2009/02/05/leveraging-annotations-in-scala.html
+// use of Transformer
+// http://www.scala-lang.org/old/sites/default/files/sids/rytz/Wed,%202010-01-27,%2015:10/annots.pdf
+// general annotations reference
+
+package chisel3.internal.naming
+
+import scala.reflect.macros.whitebox.Context
+import scala.language.experimental.macros
+import scala.annotation.StaticAnnotation
+import scala.annotation.compileTimeOnly
+
+class DebugTransforms(val c: Context) {
+ import c.universe._
+
+ /** Passthrough transform that prints the annottee for debugging purposes.
+ * No guarantees are made on what this annotation does, and it may very well change over time.
+ *
+ * The print is warning level to make it visually easier to spot, as well as a reminder that
+ * this annotation should not make it to production / committed code.
+ */
+ def dump(annottees: c.Tree*): c.Tree = {
+ val combined = annottees.map({ tree => show(tree) }).mkString("\r\n\r\n")
+ annottees.foreach(tree => c.warning(c.enclosingPosition, s"Debug dump:\n$combined"))
+ q"..$annottees"
+ }
+
+ /** Passthrough transform that prints the annottee as a tree for debugging purposes.
+ * No guarantees are made on what this annotation does, and it may very well change over time.
+ *
+ * The print is warning level to make it visually easier to spot, as well as a reminder that
+ * this annotation should not make it to production / committed code.
+ */
+ def treedump(annottees: c.Tree*): c.Tree = {
+ val combined = annottees.map({ tree => showRaw(tree) }).mkString("\r\n")
+ annottees.foreach(tree => c.warning(c.enclosingPosition, s"Debug tree dump:\n$combined"))
+ q"..$annottees"
+ }
+}
+
+class NamingTransforms(val c: Context) {
+ import c.universe._
+ import Flag._
+
+ val globalNamingStack = q"_root_.chisel3.internal.DynamicNamingStack()"
+
+ /** Base transformer that provides the val name transform.
+ * Should not be instantiated, since by default this will recurse everywhere and break the
+ * naming context variable bounds.
+ */
+ trait ValNameTransformer extends Transformer {
+ val contextVar: TermName
+
+ override def transform(tree: Tree) = tree match {
+ // Intentionally not prefixed with $mods, since modifiers usually mean the val definition
+ // is in a non-transformable location, like as a parameter list.
+ // TODO: is this exhaustive / correct in all cases?
+ case q"val $tname: $tpt = $expr" => {
+ val TermName(tnameStr: String) = tname
+ val transformedExpr = super.transform(expr)
+ q"val $tname: $tpt = $contextVar.name($transformedExpr, $tnameStr)"
+ }
+ case other => super.transform(other)
+ }
+ }
+
+ /** Module-specific val name transform, containing logic to prevent from recursing into inner
+ * classes and applies the naming transform on inner functions.
+ */
+ class ModuleTransformer(val contextVar: TermName) extends ValNameTransformer {
+ override def transform(tree: Tree) = tree match {
+ case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" =>
+ tree // don't recurse into inner classes
+ case q"$mods trait $tpname[..$tparams] extends { ..$earlydefns } with ..$parents { $self => ..$stats }" =>
+ tree // don't recurse into inner classes
+ case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => {
+ val Modifiers(_, _, annotations) = mods
+ val containsChiselName = annotations.map({q"new chiselName()" equalsStructure _}).fold(false)({_||_})
+ if (containsChiselName) {
+ tree // don't apply the transform multiple times
+ } else {
+ // apply chiselName transform by default
+ val transformedExpr = transformHierarchicalMethod(expr)
+ q"$mods def $tname[..$tparams](...$paramss): $tpt = $transformedExpr"
+ }
+ }
+ case other => super.transform(other)
+ }
+ }
+
+ /** Method-specific val name transform, handling the return case.
+ */
+ class MethodTransformer(val contextVar: TermName) extends ValNameTransformer {
+ override def transform(tree: Tree) = tree match {
+ // TODO: better error messages when returning nothing
+ case q"return $expr" => q"return $globalNamingStack.pop_return_context($expr, $contextVar)"
+ // Do not recurse into methods
+ case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => tree
+ case other => super.transform(other)
+ }
+ }
+
+ /** Applies the val name transform to a module body. Pretty straightforward, since Module is
+ * the naming top level.
+ */
+ def transformModuleBody(stats: List[c.Tree]) = {
+ val contextVar = TermName(c.freshName("namingContext"))
+ val transformedBody = (new ModuleTransformer(contextVar)).transformTrees(stats)
+
+ q"""
+ val $contextVar = $globalNamingStack.push_context()
+ ..$transformedBody
+ $contextVar.name_prefix("")
+ $globalNamingStack.pop_context($contextVar)
+ """
+ }
+
+ /** Applies the val name transform to a method body, doing additional bookkeeping with the
+ * context to allow names to propagate and prefix through the function call stack.
+ */
+ def transformHierarchicalMethod(expr: c.Tree) = {
+ val contextVar = TermName(c.freshName("namingContext"))
+ val transformedBody = (new MethodTransformer(contextVar)).transform(expr)
+
+ q"""{
+ val $contextVar = $globalNamingStack.push_context()
+ $globalNamingStack.pop_return_context($transformedBody, $contextVar)
+ }
+ """
+ }
+
+ /** Applies naming transforms to vals in the annotated module or method.
+ *
+ * For methods, a hierarchical naming transform is used, where it will try to give objects names
+ * based on the call stack, assuming all functions on the stack are annotated as such and return
+ * a non-AnyVal object. Does not recurse into inner functions.
+ *
+ * For modules, this serves as the root of the call stack hierarchy for naming purposes. Methods
+ * will have chiselName annotations (non-recursively), but this does NOT affect inner classes.
+ *
+ * Basically rewrites all instances of:
+ * val name = expr
+ * to:
+ * val name = context.name(expr, name)
+ */
+ def chiselName(annottees: c.Tree*): c.Tree = {
+ var namedElts: Int = 0
+
+ val transformed = annottees.map(annottee => annottee match {
+ case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" => {
+ val transformedStats = transformModuleBody(stats)
+ namedElts += 1
+ q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$transformedStats }"
+ }
+ case q"$mods object $tname extends { ..$earlydefns } with ..$parents { $self => ..$body }" => {
+ annottee // Don't fail noisly when a companion object is passed in with the actual class def
+ }
+ // Currently disallow on traits, this won't work well with inheritance.
+ case q"$mods def $tname[..$tparams](...$paramss): $tpt = $expr" => {
+ val transformedExpr = transformHierarchicalMethod(expr)
+ namedElts += 1
+ q"$mods def $tname[..$tparams](...$paramss): $tpt = $transformedExpr"
+ }
+ case other => c.abort(c.enclosingPosition, s"@chiselName annotion may only be used on classes and methods, got ${showCode(other)}")
+ })
+
+ if (namedElts != 1) {
+ c.abort(c.enclosingPosition, s"@chiselName annotation did not match exactly one valid tree, got:\r\n${annottees.map(tree => showCode(tree)).mkString("\r\n\r\n")}")
+ }
+
+ q"..$transformed"
+ }
+}
diff --git a/src/main/scala/chisel3/compatibility.scala b/src/main/scala/chisel3/compatibility.scala
index abbe8ffe..a919e3b9 100644
--- a/src/main/scala/chisel3/compatibility.scala
+++ b/src/main/scala/chisel3/compatibility.scala
@@ -6,6 +6,10 @@
package object Chisel { // scalastyle:ignore package.object.name
import chisel3.internal.firrtl.Width
+ import scala.language.experimental.macros
+ import scala.annotation.StaticAnnotation
+ import scala.annotation.compileTimeOnly
+
implicit val defaultCompileOptions = chisel3.core.ExplicitCompileOptions.NotStrict
type Direction = chisel3.core.Direction
@@ -333,4 +337,27 @@ package object Chisel { // scalastyle:ignore package.object.name
val Pipe = chisel3.util.Pipe
type Pipe[T <: Data] = chisel3.util.Pipe[T]
+
+ /** Package for experimental features, which may have their API changed, be removed, etc.
+ *
+ * Because its contents won't necessarily have the same level of stability and support as
+ * non-experimental, you must explicitly import this package to use its contents.
+ */
+ object experimental {
+ import scala.annotation.StaticAnnotation
+ import scala.annotation.compileTimeOnly
+
+ @compileTimeOnly("enable macro paradise to expand macro annotations")
+ class dump extends StaticAnnotation {
+ def macroTransform(annottees: Any*): Any = macro chisel3.internal.naming.DebugTransforms.dump
+ }
+ @compileTimeOnly("enable macro paradise to expand macro annotations")
+ class treedump extends StaticAnnotation {
+ def macroTransform(annottees: Any*): Any = macro chisel3.internal.naming.DebugTransforms.treedump
+ }
+ @compileTimeOnly("enable macro paradise to expand macro annotations")
+ class chiselName extends StaticAnnotation {
+ def macroTransform(annottees: Any*): Any = macro chisel3.internal.naming.NamingTransforms.chiselName
+ }
+ }
}
diff --git a/src/main/scala/chisel3/package.scala b/src/main/scala/chisel3/package.scala
index cba8dffe..109bd14e 100644
--- a/src/main/scala/chisel3/package.scala
+++ b/src/main/scala/chisel3/package.scala
@@ -285,5 +285,22 @@ package object chisel3 { // scalastyle:ignore package.object.name
*/
def range(args: Any*): (NumericBound[Int], NumericBound[Int]) = macro chisel3.internal.RangeTransform.apply
}
+
+ import scala.language.experimental.macros
+ import scala.annotation.StaticAnnotation
+ import scala.annotation.compileTimeOnly
+
+ @compileTimeOnly("enable macro paradise to expand macro annotations")
+ class dump extends StaticAnnotation {
+ def macroTransform(annottees: Any*): Any = macro chisel3.internal.naming.DebugTransforms.dump
+ }
+ @compileTimeOnly("enable macro paradise to expand macro annotations")
+ class treedump extends StaticAnnotation {
+ def macroTransform(annottees: Any*): Any = macro chisel3.internal.naming.DebugTransforms.treedump
+ }
+ @compileTimeOnly("enable macro paradise to expand macro annotations")
+ class chiselName extends StaticAnnotation {
+ def macroTransform(annottees: Any*): Any = macro chisel3.internal.naming.NamingTransforms.chiselName
+ }
}
}
diff --git a/src/test/scala/chiselTests/NamingAnnotationTest.scala b/src/test/scala/chiselTests/NamingAnnotationTest.scala
new file mode 100644
index 00000000..7b05d338
--- /dev/null
+++ b/src/test/scala/chiselTests/NamingAnnotationTest.scala
@@ -0,0 +1,200 @@
+// See LICENSE for license details.
+
+package chiselTests
+
+import chisel3._
+import chisel3.experimental.{chiselName, dump}
+import org.scalatest._
+import org.scalatest.prop._
+import chisel3.testers.BasicTester
+
+import scala.collection.mutable.ListBuffer
+
+trait NamedModuleTester extends Module {
+ val io = IO(new Bundle()) // Named module testers don't need IO
+
+ val expectedNameMap = ListBuffer[(Data, String)]()
+
+ /** Expects some name for a node that is propagated to FIRRTL.
+ * The node is returned allowing this to be called inline.
+ */
+ def expectName[T <: Data](node: T, fullName: String): T = {
+ expectedNameMap += ((node, fullName))
+ node
+ }
+
+ /** After this module has been elaborated, returns a list of (node, expected name, actual name)
+ * that did not match expectations.
+ * Returns an empty list if everything was fine.
+ */
+ def getNameFailures(): List[(Data, String, String)] = {
+ val failures = ListBuffer[(Data, String, String)]()
+ for ((ref, expectedName) <- expectedNameMap) {
+ if (ref.instanceName != expectedName) {
+ failures += ((ref, expectedName, ref.instanceName))
+ }
+ }
+ failures.toList
+ }
+}
+
+@chiselName
+class NamedModule extends NamedModuleTester {
+ @chiselName
+ def FunctionMockupInner(): UInt = {
+ val my2A = 1.U
+ val my2B = expectName(my2A +& 2.U, "test_myNested_my2B")
+ val my2C = my2B +& 3.U // should get named at enclosing scope
+ my2C
+ }
+
+ @chiselName
+ def FunctionMockup(): UInt = {
+ val myNested = expectName(FunctionMockupInner(), "test_myNested")
+ val myA = expectName(1.U + myNested, "test_myA")
+ val myB = expectName(myA +& 2.U, "test_myB")
+ val myC = expectName(myB +& 3.U, "test_myC")
+ myC +& 4.U // named at enclosing scope
+ }
+
+ // chiselName "implicitly" applied
+ def ImplicitlyNamed(): UInt = {
+ val implicitA = expectName(1.U + 2.U, "test3_implicitA")
+ val implicitB = expectName(implicitA + 3.U, "test3_implicitB")
+ implicitB + 2.U // named at enclosing scope
+ }
+
+ // Ensure this applies a partial name if there is no return value
+ def NoReturnFunction() {
+ val noreturn = expectName(1.U + 2.U, "noreturn")
+ }
+
+
+ val test = expectName(FunctionMockup(), "test")
+ val test2 = expectName(test +& 2.U, "test2")
+ val test3 = expectName(ImplicitlyNamed(), "test3")
+
+ // Test that contents of for loops are named
+ for (i <- 0 until 1) {
+ val forInner = expectName(test3 + i.U, "forInner")
+ }
+
+ // Test that contents of anonymous functions are named
+ Seq((0, "anonInner"), (1, "anonInner_1"), (2, "anonInner_2")).foreach { case (in, name) =>
+ val anonInner = expectName(test3 + in.U, name)
+ }
+
+ NoReturnFunction()
+}
+
+@chiselName
+class NameCollisionModule extends NamedModuleTester {
+ @chiselName
+ def repeatedCalls(id: Int): UInt = {
+ val test = expectName(1.U + 3.U, s"test_$id") // should disambiguate by invocation order
+ test + 2.U
+ }
+
+ // chiselName applied by default to this
+ def innerNamedFunction() {
+ // ... but not this inner function
+ def innerUnnamedFunction() {
+ val a = repeatedCalls(1)
+ val b = repeatedCalls(2)
+ }
+
+ innerUnnamedFunction()
+ }
+
+ val test = expectName(1.U + 2.U, "test")
+ innerNamedFunction()
+}
+
+/** Ensure no crash happens if a named function is enclosed in a non-named module
+ */
+class NonNamedModule extends NamedModuleTester {
+ @chiselName
+ def NamedFunction(): UInt = {
+ val myVal = 1.U + 2.U
+ myVal
+ }
+
+ val test = NamedFunction()
+}
+
+/** Ensure no crash happens if a named function is enclosed in a non-named function in a named
+ * module.
+ */
+object NonNamedHelper {
+ @chiselName
+ def NamedFunction(): UInt = {
+ val myVal = 1.U + 2.U
+ myVal
+ }
+
+ def NonNamedFunction() : UInt = {
+ val myVal = NamedFunction()
+ myVal
+ }
+}
+
+@chiselName
+class NonNamedFunction extends NamedModuleTester {
+ val test = NonNamedHelper.NamedFunction()
+}
+
+/** Ensure broken links in the chain are simply dropped
+ */
+@chiselName
+class PartialNamedModule extends NamedModuleTester {
+ // Create an inner function that is the extent of the implicit naming
+ def innerNamedFunction(): UInt = {
+ def innerUnnamedFunction(): UInt = {
+ @chiselName
+ def disconnectedNamedFunction(): UInt = {
+ val a = expectName(1.U + 2.U, "test_a")
+ val b = expectName(a + 2.U, "test_b")
+ b
+ }
+ disconnectedNamedFunction()
+ }
+ innerUnnamedFunction() + 1.U
+ }
+
+ val test = innerNamedFunction()
+}
+
+
+/** A simple test that checks the recursive function val naming annotation both compiles and
+ * generates the expected names.
+ */
+class NamingAnnotationSpec extends ChiselPropSpec {
+ property("NamedModule should have function hierarchical names") {
+ // TODO: clean up test style
+ var module: NamedModule = null
+ elaborate { module = new NamedModule; module }
+ assert(module.getNameFailures() == Nil)
+ }
+
+ property("NameCollisionModule should disambiguate collisions") {
+ // TODO: clean up test style
+ var module: NameCollisionModule = null
+ elaborate { module = new NameCollisionModule; module }
+ assert(module.getNameFailures() == Nil)
+ }
+
+ property("PartialNamedModule should have partial names") {
+ // TODO: clean up test style
+ var module: PartialNamedModule = null
+ elaborate { module = new PartialNamedModule; module }
+ assert(module.getNameFailures() == Nil)
+ }
+
+ property("NonNamedModule should elaborate") {
+ elaborate { new NonNamedModule }
+ }
+
+ property("NonNamedFunction should elaborate") {
+ elaborate { new NonNamedFunction }
+ }
+}