summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRichard Lin2017-01-25 21:02:04 -0800
committerJack Koenig2017-01-25 21:02:04 -0800
commitfa395b4c22f75d66047c1e835413beb612bc31d3 (patch)
treeffe080afef6e77126ad80e4a11340383cff55b2e /src
parent2e6444cc55b54b59f781a14823e219d9a2413f72 (diff)
Better name propagation by macros (#327)
* Name propagation * chiselName everywhere at best-effort level * Better collision handling * Allow recursing into inner anonymous functions * Add for loop and anonymous inner function tests
Diffstat (limited to 'src')
-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
3 files changed, 244 insertions, 0 deletions
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 }
+ }
+}