-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
LazyModule.scala
163 lines (139 loc) · 6.65 KB
/
LazyModule.scala
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
163
// See LICENSE.SiFive for license details.
package diplomacy
import Chisel._
import config._
import chisel3.experimental.{BaseModule, RawModule, MultiIOModule}
import chisel3.internal.sourceinfo.{SourceInfo, SourceLine, UnlocatableSourceInfo}
abstract class LazyModule()(implicit val p: Parameters)
{
protected[diplomacy] var bindings = List[() => Unit]()
protected[diplomacy] var children = List[LazyModule]()
protected[diplomacy] var nodes = List[BaseNode]()
protected[diplomacy] var info: SourceInfo = UnlocatableSourceInfo
protected[diplomacy] val parent = LazyModule.stack.headOption
LazyModule.stack = this :: LazyModule.stack
parent.foreach(p => p.children = this :: p.children)
private var suggestedName: Option[String] = None
def suggestName(x: String) = suggestedName = Some(x)
private lazy val childNames =
getClass.getMethods.filter { m =>
m.getParameterTypes.isEmpty &&
!java.lang.reflect.Modifier.isStatic(m.getModifiers) &&
m.getName != "children"
}.flatMap { m =>
if (classOf[LazyModule].isAssignableFrom(m.getReturnType)) {
val obj = m.invoke(this)
if (obj eq null) Seq() else Seq((m.getName, obj))
} else if (classOf[Seq[LazyModule]].isAssignableFrom(m.getReturnType)) {
val obj = m.invoke(this)
if (obj eq null) Seq() else {
val seq = try { obj.asInstanceOf[Seq[Object]] } catch { case _: Throwable => null }
if (seq eq null) Seq() else {
seq.zipWithIndex.map { case (l, i) => (m.getName + "_" + i, l) }
}
}
} else Seq()
}
private def findValName =
parent.flatMap(_.childNames.find(_._2 eq this)).map(_._1)
lazy val className = getClass.getName.split('.').last
lazy val valName = suggestedName.orElse(findValName)
lazy val outerName = if (nodes.size != 1) None else nodes(0).gco.flatMap(_.lazyModule.valName)
def moduleName = className + valName.orElse(outerName).map("_" + _).getOrElse("")
def instanceName = valName.getOrElse(outerName.map(_ + "_").getOrElse("") + className)
def name = valName.getOrElse(className)
def line = sourceLine(info)
def module: LazyModuleImpLike
protected[diplomacy] def instantiate() = {
children.reverse.foreach { c =>
// !!! fix chisel3 so we can pass the desired sourceInfo
// implicit val sourceInfo = c.module.outer.info
Module(c.module)
}
bindings.reverse.foreach { f => f () }
}
def omitGraphML: Boolean = !nodes.exists(!_.omitGraphML) && !children.exists(!_.omitGraphML)
lazy val graphML: String = parent.map(_.graphML).getOrElse {
val buf = new StringBuilder
buf ++= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
buf ++= "<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:y=\"http://www.yworks.com/xml/graphml\">\n"
buf ++= " <key for=\"node\" id=\"n\" yfiles.type=\"nodegraphics\"/>\n"
buf ++= " <key for=\"edge\" id=\"e\" yfiles.type=\"edgegraphics\"/>\n"
buf ++= " <key for=\"node\" id=\"d\" attr.name=\"NodeDebugString\" attr.type=\"string\"/>\n"
buf ++= " <graph id=\"G\" edgedefault=\"directed\">\n"
nodesGraphML(buf, " ")
edgesGraphML(buf, " ")
buf ++= " </graph>\n"
buf ++= "</graphml>\n"
buf.toString
}
private val index = { LazyModule.index = LazyModule.index + 1; LazyModule.index }
private def nodesGraphML(buf: StringBuilder, pad: String) {
buf ++= s"""${pad}<node id=\"${index}\">\n"""
buf ++= s"""${pad} <data key=\"n\"><y:ShapeNode><y:NodeLabel modelName=\"sides\" modelPosition=\"w\" rotationAngle=\"270.0\">${module.instanceName}</y:NodeLabel></y:ShapeNode></data>\n"""
buf ++= s"""${pad} <graph id=\"${index}::\" edgedefault=\"directed\">\n"""
nodes.filter(!_.omitGraphML).foreach { n =>
buf ++= s"""${pad} <node id=\"${index}::${n.index}\">\n"""
buf ++= s"""${pad} <data key=\"d\"><y:ShapeNode><y:Shape type="ellipse"/></y:ShapeNode>${n.nodedebugstring}</data>\n"""
buf ++= s"""${pad} </node>\n"""
}
children.filter(!_.omitGraphML).foreach { _.nodesGraphML(buf, pad + " ") }
buf ++= s"""${pad} </graph>\n"""
buf ++= s"""${pad}</node>\n"""
}
private def edgesGraphML(buf: StringBuilder, pad: String) {
nodes.filter(!_.omitGraphML) foreach { n => n.outputs.filter(!_._1.omitGraphML).foreach { case (o, l) =>
buf ++= pad
buf ++= "<edge"
if (o.reverse) {
buf ++= s""" target=\"${index}::${n.index}\""""
buf ++= s""" source=\"${o.lazyModule.index}::${o.index}\">"""
} else {
buf ++= s""" source=\"${index}::${n.index}\""""
buf ++= s""" target=\"${o.lazyModule.index}::${o.index}\">"""
}
buf ++= s"""<data key=\"e\"><y:PolyLineEdge>"""
if (o.reverse) {
buf ++= s"""<y:Arrows source=\"standard\" target=\"none\"/>"""
} else {
buf ++= s"""<y:Arrows source=\"none\" target=\"standard\"/>"""
}
buf ++= s"""<y:LineStyle color=\"${o.colour}\" type=\"line\" width=\"1.0\"/>"""
buf ++= s"""<y:EdgeLabel modelName=\"centered\" rotationAngle=\"270.0\">${l}</y:EdgeLabel>"""
buf ++= s"""</y:PolyLineEdge></data></edge>\n"""
} }
children.filter(!_.omitGraphML).foreach { c => c.edgesGraphML(buf, pad) }
}
def nodeIterator(iterfunc: (LazyModule) => Unit): Unit = {
iterfunc(this)
children.foreach( _.nodeIterator(iterfunc) )
}
}
object LazyModule
{
protected[diplomacy] var stack = List[LazyModule]()
private var index = 0
def apply[T <: LazyModule](bc: T)(implicit sourceInfo: SourceInfo): T = {
// Make sure the user put LazyModule around modules in the correct order
// If this require fails, probably some grandchild was missing a LazyModule
// ... or you applied LazyModule twice
require (!stack.isEmpty, s"LazyModule() applied to ${bc.name} twice ${sourceLine(sourceInfo)}")
require (stack.head eq bc, s"LazyModule() applied to ${bc.name} before ${stack.head.name} ${sourceLine(sourceInfo)}")
stack = stack.tail
bc.info = sourceInfo
bc
}
}
trait LazyModuleImpLike extends BaseModule
{
val wrapper: LazyModule
// .module had better not be accessed while LazyModules are still being built!
require (LazyModule.stack.isEmpty, s"${wrapper.name}.module was constructed before LazyModule() was run on ${LazyModule.stack.head.name}")
override def desiredName = wrapper.moduleName
suggestName(wrapper.instanceName)
wrapper.instantiate()
implicit val p = wrapper.p
}
abstract class LazyModuleImp(val wrapper: LazyModule) extends Module with LazyModuleImpLike
abstract class LazyMultiIOModuleImp(val wrapper: LazyModule) extends MultiIOModule with LazyModuleImpLike
abstract class LazyRawModuleImp(val wrapper: LazyModule) extends RawModule with LazyModuleImpLike