1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
|
// SPDX-License-Identifier: Apache-2.0
// This file contains part of the implementation of the naming static annotation system.
package chisel3.internal.naming
import chisel3.experimental.NoChiselNamePrefix
import scala.collection.mutable.Stack
import scala.collection.mutable.ListBuffer
import java.util.IdentityHashMap
import scala.collection.JavaConverters._
/** 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.
*/
sealed trait NamingContextInterface {
/** 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
/** 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 namePrefix(prefix: String)
}
/** Dummy implementation to allow for naming annotations in a non-Builder context.
*/
object DummyNamer extends NamingContextInterface {
def name[T](obj: T, name: String): T = obj
def namePrefix(prefix: String): Unit = {}
}
/** Actual namer functionality.
*/
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 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
* scope of this context.
*/
def addDescendant(ref: Any, descendant: NamingContext) {
ref match {
case ref: AnyRef =>
// getOrElseUpdate
val l = descendants.get(ref)
val buf =
if (l != null) l
else {
val value = ListBuffer[NamingContext]()
descendants.put(ref, value)
value
}
buf += descendant
case _ => anonymousDescendants += descendant
}
}
def name[T](obj: T, name: String): T = {
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))
case _ =>
}
obj
}
def namePrefix(prefix: String): Unit = {
closed = true
for ((ref, suffix) <- items) {
// First name the top-level object
chisel3.internal.Builder.nameRecursively(prefix + suffix, ref, (id, name) => id.suggestName(name))
// Then recurse into descendant contexts
if (descendants.containsKey(ref)) {
for (descendant <- descendants.get(ref)) {
descendant.namePrefix(prefix + suffix + "_")
}
descendants.remove(ref)
}
}
for (descendant <- descendants.values.asScala.flatten) {
// Where we have a broken naming link, just ignore the missing parts
descendant.namePrefix(prefix)
}
for (descendant <- anonymousDescendants) {
descendant.namePrefix(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 namingStack = 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 pushContext(): NamingContext = {
val context = new NamingContext
namingStack.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 popContext[T <: Any](prefixRef: T, until: NamingContext): Unit = {
assert(namingStack.top == until)
namingStack.pop()
if (!namingStack.isEmpty) {
namingStack.top.addDescendant(prefixRef, until)
}
}
def length(): Int = namingStack.length
}
|