2016-11-28 01:16:37 +01:00
|
|
|
// See LICENSE.SiFive for license details.
|
2016-08-19 20:08:35 +02:00
|
|
|
|
2017-07-07 19:48:16 +02:00
|
|
|
package freechips.rocketchip.diplomacy
|
2016-08-19 20:08:35 +02:00
|
|
|
|
|
|
|
import Chisel._
|
2017-08-06 22:40:07 +02:00
|
|
|
import chisel3.experimental.{BaseModule, RawModule, MultiIOModule, withClockAndReset}
|
2016-09-08 23:41:08 +02:00
|
|
|
import chisel3.internal.sourceinfo.{SourceInfo, SourceLine, UnlocatableSourceInfo}
|
2017-07-07 19:48:16 +02:00
|
|
|
import freechips.rocketchip.config.Parameters
|
2017-09-14 03:06:03 +02:00
|
|
|
import scala.collection.immutable.ListMap
|
2016-08-19 20:08:35 +02:00
|
|
|
|
2016-12-02 02:46:52 +01:00
|
|
|
abstract class LazyModule()(implicit val p: Parameters)
|
2016-08-19 20:08:35 +02:00
|
|
|
{
|
2016-10-04 00:17:36 +02:00
|
|
|
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
|
2016-09-02 20:13:43 +02:00
|
|
|
|
|
|
|
LazyModule.stack = this :: LazyModule.stack
|
|
|
|
parent.foreach(p => p.children = this :: p.children)
|
2016-08-19 20:08:35 +02:00
|
|
|
|
2017-01-25 03:10:37 +01:00
|
|
|
private var suggestedName: Option[String] = None
|
|
|
|
def suggestName(x: String) = suggestedName = Some(x)
|
|
|
|
|
|
|
|
private lazy val childNames =
|
|
|
|
getClass.getMethods.filter { m =>
|
2016-10-15 00:11:13 +02:00
|
|
|
m.getParameterTypes.isEmpty &&
|
|
|
|
!java.lang.reflect.Modifier.isStatic(m.getModifiers) &&
|
2017-01-25 03:10:37 +01:00
|
|
|
m.getName != "children"
|
|
|
|
}.flatMap { m =>
|
|
|
|
if (classOf[LazyModule].isAssignableFrom(m.getReturnType)) {
|
2017-01-30 23:25:12 +01:00
|
|
|
val obj = m.invoke(this)
|
|
|
|
if (obj eq null) Seq() else Seq((m.getName, obj))
|
2017-01-25 03:10:37 +01:00
|
|
|
} else if (classOf[Seq[LazyModule]].isAssignableFrom(m.getReturnType)) {
|
2017-01-30 23:25:12 +01:00
|
|
|
val obj = m.invoke(this)
|
|
|
|
if (obj eq null) Seq() else {
|
2017-02-22 20:28:04 +01:00
|
|
|
val seq = try { obj.asInstanceOf[Seq[Object]] } catch { case _: Throwable => null }
|
2017-01-30 23:25:12 +01:00
|
|
|
if (seq eq null) Seq() else {
|
|
|
|
seq.zipWithIndex.map { case (l, i) => (m.getName + "_" + i, l) }
|
|
|
|
}
|
2017-01-25 03:10:37 +01:00
|
|
|
}
|
|
|
|
} 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)
|
2016-10-15 01:32:39 +02:00
|
|
|
lazy val outerName = if (nodes.size != 1) None else nodes(0).gco.flatMap(_.lazyModule.valName)
|
2016-10-15 00:11:13 +02:00
|
|
|
|
2016-10-15 01:32:39 +02:00
|
|
|
def moduleName = className + valName.orElse(outerName).map("_" + _).getOrElse("")
|
2016-10-25 23:29:54 +02:00
|
|
|
def instanceName = valName.getOrElse(outerName.map(_ + "_").getOrElse("") + className)
|
2016-10-15 00:11:13 +02:00
|
|
|
def name = valName.getOrElse(className)
|
2016-09-09 06:11:31 +02:00
|
|
|
def line = sourceLine(info)
|
2016-09-08 23:41:08 +02:00
|
|
|
|
2017-05-02 10:36:38 +02:00
|
|
|
def module: LazyModuleImpLike
|
2016-08-19 20:08:35 +02:00
|
|
|
|
2017-04-15 01:35:43 +02:00
|
|
|
def omitGraphML: Boolean = !nodes.exists(!_.omitGraphML) && !children.exists(!_.omitGraphML)
|
2016-09-26 10:19:28 +02:00
|
|
|
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"
|
2016-10-03 23:07:28 +02:00
|
|
|
buf ++= " <key for=\"node\" id=\"n\" yfiles.type=\"nodegraphics\"/>\n"
|
|
|
|
buf ++= " <key for=\"edge\" id=\"e\" yfiles.type=\"edgegraphics\"/>\n"
|
2017-04-14 23:21:22 +02:00
|
|
|
buf ++= " <key for=\"node\" id=\"d\" attr.name=\"NodeDebugString\" attr.type=\"string\"/>\n"
|
2016-09-26 10:19:28 +02:00
|
|
|
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"""
|
2016-10-28 23:00:55 +02:00
|
|
|
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"""
|
2016-09-26 10:19:28 +02:00
|
|
|
buf ++= s"""${pad} <graph id=\"${index}::\" edgedefault=\"directed\">\n"""
|
|
|
|
nodes.filter(!_.omitGraphML).foreach { n =>
|
2017-04-14 23:21:22 +02:00
|
|
|
buf ++= s"""${pad} <node id=\"${index}::${n.index}\">\n"""
|
2017-04-15 02:01:12 +02:00
|
|
|
buf ++= s"""${pad} <data key=\"d\"><y:ShapeNode><y:Shape type="ellipse"/></y:ShapeNode>${n.nodedebugstring}</data>\n"""
|
2017-04-14 23:21:22 +02:00
|
|
|
buf ++= s"""${pad} </node>\n"""
|
2016-09-26 10:19:28 +02:00
|
|
|
}
|
|
|
|
children.filter(!_.omitGraphML).foreach { _.nodesGraphML(buf, pad + " ") }
|
|
|
|
buf ++= s"""${pad} </graph>\n"""
|
|
|
|
buf ++= s"""${pad}</node>\n"""
|
|
|
|
}
|
|
|
|
private def edgesGraphML(buf: StringBuilder, pad: String) {
|
2016-10-28 23:00:55 +02:00
|
|
|
nodes.filter(!_.omitGraphML) foreach { n => n.outputs.filter(!_._1.omitGraphML).foreach { case (o, l) =>
|
2016-09-26 10:19:28 +02:00
|
|
|
buf ++= pad
|
|
|
|
buf ++= "<edge"
|
2017-04-15 01:17:56 +02:00
|
|
|
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\"/>"""
|
|
|
|
}
|
2016-10-28 23:00:55 +02:00
|
|
|
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"""
|
2016-09-26 10:19:28 +02:00
|
|
|
} }
|
|
|
|
children.filter(!_.omitGraphML).foreach { c => c.edgesGraphML(buf, pad) }
|
|
|
|
}
|
2017-04-14 23:21:22 +02:00
|
|
|
|
2017-01-13 00:20:32 +01:00
|
|
|
def nodeIterator(iterfunc: (LazyModule) => Unit): Unit = {
|
|
|
|
iterfunc(this)
|
|
|
|
children.foreach( _.nodeIterator(iterfunc) )
|
|
|
|
}
|
2016-09-02 20:13:43 +02:00
|
|
|
}
|
2016-09-01 01:45:18 +02:00
|
|
|
|
2016-09-02 20:13:43 +02:00
|
|
|
object LazyModule
|
|
|
|
{
|
2016-10-04 00:17:36 +02:00
|
|
|
protected[diplomacy] var stack = List[LazyModule]()
|
2016-09-26 10:19:28 +02:00
|
|
|
private var index = 0
|
2016-09-02 20:13:43 +02:00
|
|
|
|
|
|
|
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
|
2016-09-09 06:11:31 +02:00
|
|
|
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)}")
|
2016-09-02 20:13:43 +02:00
|
|
|
stack = stack.tail
|
|
|
|
bc.info = sourceInfo
|
|
|
|
bc
|
|
|
|
}
|
2016-08-19 20:08:35 +02:00
|
|
|
}
|
|
|
|
|
2017-08-07 02:32:23 +02:00
|
|
|
sealed trait LazyModuleImpLike extends BaseModule
|
2016-08-30 02:53:31 +02:00
|
|
|
{
|
2017-05-02 10:36:38 +02:00
|
|
|
val wrapper: LazyModule
|
2017-09-14 03:06:03 +02:00
|
|
|
val auto: AutoBundle
|
|
|
|
protected[diplomacy] val dangles: Seq[Dangle]
|
2017-05-02 10:36:38 +02:00
|
|
|
|
2016-09-02 20:13:43 +02:00
|
|
|
// .module had better not be accessed while LazyModules are still being built!
|
2017-05-02 10:36:38 +02:00
|
|
|
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)
|
2016-09-02 20:13:43 +02:00
|
|
|
|
2017-05-02 10:36:38 +02:00
|
|
|
implicit val p = wrapper.p
|
|
|
|
|
2017-09-14 03:06:03 +02:00
|
|
|
protected[diplomacy] def instantiate() = {
|
|
|
|
val childDangles = wrapper.children.reverse.flatMap { c =>
|
|
|
|
implicit val sourceInfo = c.info
|
|
|
|
Module(c.module).dangles
|
|
|
|
}
|
|
|
|
val nodeDangles = wrapper.nodes.reverse.flatMap(_.instantiate())
|
|
|
|
val (toConnect, toForward) = (nodeDangles ++ childDangles).groupBy(_.source).partition(_._2.size == 2)
|
|
|
|
val forward = toForward.map(_._2(0)).toList
|
|
|
|
toConnect.foreach { case (_, Seq(a, b)) =>
|
|
|
|
require (a.flipped != b.flipped)
|
|
|
|
if (a.flipped) { a.data <> b.data } else { b.data <> a.data }
|
|
|
|
}
|
|
|
|
val auto = IO(new AutoBundle(forward.map { d => (d.name, d.data, d.flipped) }:_*))
|
|
|
|
val dangles = (forward zip auto.elements) map { case (d, (_, io)) =>
|
|
|
|
if (d.flipped) { d.data <> io } else { io <> d.data }
|
|
|
|
d.copy(data = io, name = wrapper.valName.getOrElse("anon") + "_" + d.name)
|
|
|
|
}
|
|
|
|
wrapper.bindings.reverse.foreach { f => f () }
|
|
|
|
(auto, dangles)
|
|
|
|
}
|
2017-08-06 22:40:07 +02:00
|
|
|
}
|
2017-05-02 10:36:38 +02:00
|
|
|
|
2017-09-14 03:06:03 +02:00
|
|
|
abstract class LazyModuleImp(val wrapper: LazyModule) extends MultiIOModule with LazyModuleImpLike {
|
|
|
|
val (auto, dangles) = instantiate()
|
2017-08-06 22:40:07 +02:00
|
|
|
}
|
2017-05-02 10:36:38 +02:00
|
|
|
|
2017-08-06 22:40:07 +02:00
|
|
|
abstract class LazyRawModuleImp(val wrapper: LazyModule) extends RawModule with LazyModuleImpLike {
|
2017-09-14 03:06:03 +02:00
|
|
|
val (auto, dangles) = withClockAndReset(Bool(false).asClock, Bool(true)) {
|
|
|
|
instantiate()
|
2017-08-06 22:40:07 +02:00
|
|
|
}
|
|
|
|
}
|
2017-09-14 03:06:03 +02:00
|
|
|
|
|
|
|
case class HalfEdge(serial: Int, index: Int)
|
|
|
|
case class Dangle(source: HalfEdge, sink: HalfEdge, flipped: Boolean, name: String, data: Data)
|
|
|
|
|
|
|
|
final class AutoBundle(elts: (String, Data, Boolean)*) extends Record {
|
|
|
|
// !!! need to fix-up name collision better than appending _#
|
|
|
|
val elements = ListMap(elts.zipWithIndex map { case ((field, elt, flip), i) =>
|
|
|
|
(field + "_" + i) -> (if (flip) elt.cloneType.flip else elt.cloneType)
|
|
|
|
}:_*)
|
|
|
|
|
|
|
|
override def cloneType = (new AutoBundle(elts:_*)).asInstanceOf[this.type]
|
|
|
|
}
|