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
|
// See LICENSE for license details.
package chisel3.internal
import scala.util.DynamicVariable
import scala.collection.mutable.{ArrayBuffer, HashMap}
import chisel3._
import core._
import firrtl._
private[chisel3] class Namespace(parent: Option[Namespace], keywords: Set[String]) {
private val names = collection.mutable.HashMap[String, Long]()
for (keyword <- keywords)
names(keyword) = 1
private def rename(n: String): String = {
val index = names.getOrElse(n, 1L)
val tryName = s"${n}_${index}"
names(n) = index + 1
if (this contains tryName) rename(n) else tryName
}
private def sanitize(s: String): String = {
// TODO what character set does FIRRTL truly support? using ANSI C for now
def legalStart(c: Char) = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
def legal(c: Char) = legalStart(c) || (c >= '0' && c <= '9')
val res = s filter legal
if (res.isEmpty || !legalStart(res.head)) s"_$res" else res
}
def contains(elem: String): Boolean = {
names.contains(elem) || parent.map(_ contains elem).getOrElse(false)
}
def name(elem: String): String = {
val sanitized = sanitize(elem)
if (this contains sanitized) {
name(rename(sanitized))
} else {
names(sanitized) = 1
sanitized
}
}
def child(kws: Set[String]): Namespace = new Namespace(Some(this), kws)
def child: Namespace = child(Set())
}
private[chisel3] class IdGen {
private var counter = -1L
def next: Long = {
counter += 1
counter
}
}
private[chisel3] trait HasId {
private[chisel3] def _onModuleClose: Unit = {} // scalastyle:ignore method.name
private[chisel3] val _parent: Option[Module] = Builder.currentModule
_parent.foreach(_.addId(this))
private[chisel3] val _id: Long = Builder.idGen.next
override def hashCode: Int = _id.toInt
override def equals(that: Any): Boolean = that match {
case x: HasId => _id == x._id
case _ => false
}
// 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) }
this
}
private[chisel3] def addPostnameHook(hook: String=>Unit): Unit = postname_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 Bundle)
private[chisel3] def forceName(default: =>String, namespace: Namespace): Unit =
if(_ref.isEmpty) {
val candidate_name = suggested_name.getOrElse(default)
val available_name = namespace.name(candidate_name)
setRef(Ref(available_name))
}
private var _ref: Option[Arg] = None
private[chisel3] def setRef(imm: Arg): Unit = _ref = Some(imm)
private[chisel3] def setRef(parent: HasId, name: String): Unit = setRef(Slot(Node(parent), name))
private[chisel3] def setRef(parent: HasId, index: Int): Unit = setRef(Index(Node(parent), ILit(index)))
private[chisel3] def setRef(parent: HasId, index: UInt): Unit = setRef(Index(Node(parent), index.ref))
private[chisel3] def getRef: Arg = _ref.get
}
private[chisel3] class DynamicContext {
val idGen = new IdGen
val globalNamespace = new Namespace(None, Set())
val components = ArrayBuffer[Component]()
var currentModule: Option[Module] = None
val errors = new ErrorLog
}
private[chisel3] object Builder {
// All global mutable state must be referenced via dynamicContextVar!!
private val dynamicContextVar = new DynamicVariable[Option[DynamicContext]](None)
private def dynamicContext: DynamicContext =
dynamicContextVar.value.getOrElse(new DynamicContext)
def idGen: IdGen = dynamicContext.idGen
def globalNamespace: Namespace = dynamicContext.globalNamespace
def components: ArrayBuffer[Component] = dynamicContext.components
def currentModule: Option[Module] = dynamicContext.currentModule
def currentModule_=(target: Option[Module]): Unit = {
dynamicContext.currentModule = target
}
def forcedModule: Module = currentModule match {
case Some(module) => module
case None => throw new Exception(
"Error: Not in a Module. Likely cause: Missed Module() wrap or bare chisel API call."
// A bare api call is, e.g. calling Wire() from the scala console).
)
}
// TODO(twigg): Ideally, binding checks and new bindings would all occur here
// However, rest of frontend can't support this yet.
def pushCommand[T <: Command](c: T): T = {
forcedModule._commands += c
c
}
def pushOp[T <: Data](cmd: DefPrim[T]): T = {
// Bind each element of the returned Data to being a Op
Binding.bind(cmd.id, OpBinder(forcedModule), "Error: During op creation, fresh result")
pushCommand(cmd).id
}
def errors: ErrorLog = dynamicContext.errors
def error(m: => String): Unit = errors.error(m)
def build[T <: Module](f: => T): Circuit = {
dynamicContextVar.withValue(Some(new DynamicContext)) {
errors.info("Elaborating design...")
val mod = f
mod.forceName(mod.name, globalNamespace)
errors.checkpoint()
errors.info("Done elaborating.")
Circuit(components.last.name, components)
}
}
}
|