// See LICENSE.SiFive for license details. package freechips.rocketchip.diplomacy import Chisel._ import chisel3.experimental.{BaseModule, RawModule, MultiIOModule, withClockAndReset} import chisel3.internal.sourceinfo.{SourceInfo, SourceLine, UnlocatableSourceInfo} import freechips.rocketchip.config.Parameters import scala.collection.immutable.{SortedMap,ListMap} import scala.util.matching._ abstract class LazyModule()(implicit val p: Parameters) { protected[diplomacy] var children = List[LazyModule]() protected[diplomacy] var nodes = List[BaseNode]() protected[diplomacy] var info: SourceInfo = UnlocatableSourceInfo protected[diplomacy] val parent = LazyModule.scope def parents: Seq[LazyModule] = parent match { case None => Nil case Some(x) => x +: x.parents } LazyModule.scope = Some(this) parent.foreach(p => p.children = this :: p.children) // suggestedName accumulates Some(names), taking the final one. Nones are ignored. private var suggestedNameVar: Option[String] = None def suggestName(x: String): this.type = suggestName(Some(x)) def suggestName(x: Option[String]): this.type = { x.foreach { n => suggestedNameVar = Some(n) } this } private def findClassName(c: Class[_]): String = { val n = c.getName.split('.').last if (n.contains('$')) findClassName(c.getSuperclass) else n } lazy val className = findClassName(getClass) lazy val suggestedName = suggestedNameVar.getOrElse(className) lazy val desiredName = className // + hashcode? def name = suggestedName // className + suggestedName ++ hashcode ? def line = sourceLine(info) // Accessing these names can only be done after circuit elaboration! lazy val moduleName = module.name // The final Verilog Module name lazy val pathName = module.pathName lazy val instanceName = pathName.split('.').last // The final Verilog instance name def instantiate() { } // a hook for running things in module scope (after children exist, but before dangles+auto exists) def module: LazyModuleImpLike def omitGraphML: Boolean = !nodes.exists(!_.omitGraphML) && !children.exists(!_.omitGraphML) lazy val graphML: String = parent.map(_.graphML).getOrElse { val buf = new StringBuilder buf ++= "\n" buf ++= "\n" buf ++= " \n" buf ++= " \n" buf ++= " \n" buf ++= " \n" nodesGraphML(buf, " ") edgesGraphML(buf, " ") buf ++= " \n" buf ++= "\n" buf.toString } private val index = { LazyModule.index = LazyModule.index + 1; LazyModule.index } private def nodesGraphML(buf: StringBuilder, pad: String) { buf ++= s"""${pad}\n""" buf ++= s"""${pad} ${instanceName}\n""" buf ++= s"""${pad} ${moduleName} (${pathName})\n""" buf ++= s"""${pad} \n""" nodes.filter(!_.omitGraphML).foreach { n => buf ++= s"""${pad} \n""" buf ++= s"""${pad} \n""" buf ++= s"""${pad} ${n.nodedebugstring}\n""" buf ++= s"""${pad} \n""" } children.filter(!_.omitGraphML).foreach { _.nodesGraphML(buf, pad + " ") } buf ++= s"""${pad} \n""" buf ++= s"""${pad}\n""" } private def edgesGraphML(buf: StringBuilder, pad: String) { nodes.filter(!_.omitGraphML) foreach { n => n.outputs.filter(!_._1.omitGraphML).foreach { case (o, edge) => val RenderedEdge(colour, label, flipped) = edge buf ++= pad buf ++= """" } else { buf ++= s""" source=\"${index}::${n.index}\"""" buf ++= s""" target=\"${o.lazyModule.index}::${o.index}\">""" } buf ++= s"""""" if (flipped) { buf ++= s"""""" } else { buf ++= s"""""" } buf ++= s"""""" buf ++= s"""${label}""" buf ++= s"""\n""" } } children.filter(!_.omitGraphML).foreach { c => c.edgesGraphML(buf, pad) } } def nodeIterator(iterfunc: (LazyModule) => Unit): Unit = { iterfunc(this) children.foreach( _.nodeIterator(iterfunc) ) } def getChildren = children } object LazyModule { protected[diplomacy] var scope: Option[LazyModule] = None private var index = 0 def apply[T <: LazyModule](bc: T)(implicit valName: ValName, 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 (scope.isDefined, s"LazyModule() applied to ${bc.name} twice ${sourceLine(sourceInfo)}") require (scope.get eq bc, s"LazyModule() applied to ${bc.name} before ${scope.get.name} ${sourceLine(sourceInfo)}") scope = bc.parent bc.info = sourceInfo if (!bc.suggestedNameVar.isDefined) bc.suggestName(valName.name) bc } } sealed trait LazyModuleImpLike extends BaseModule { val wrapper: LazyModule val auto: AutoBundle protected[diplomacy] val dangles: Seq[Dangle] // .module had better not be accessed while LazyModules are still being built! require (!LazyModule.scope.isDefined, s"${wrapper.name}.module was constructed before LazyModule() was run on ${LazyModule.scope.get.name}") override def desiredName = wrapper.desiredName suggestName(wrapper.suggestedName) implicit val p = wrapper.p protected[diplomacy] def instantiate() = { val childDangles = wrapper.children.reverse.flatMap { c => implicit val sourceInfo = c.info val mod = Module(c.module) mod.finishInstantiate() mod.dangles } val nodeDangles = wrapper.nodes.reverse.flatMap(_.instantiate()) val allDangles = nodeDangles ++ childDangles val pairing = SortedMap(allDangles.groupBy(_.source).toSeq:_*) val done = Set() ++ pairing.values.filter(_.size == 2).map { case Seq(a, b) => require (a.flipped != b.flipped) if (a.flipped) { a.data <> b.data } else { b.data <> a.data } a.source } val forward = allDangles.filter(d => !done(d.source)) 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.suggestedName + "_" + d.name) } wrapper.instantiate() (auto, dangles) } protected[diplomacy] def finishInstantiate() { wrapper.nodes.reverse.foreach { _.finishInstantiate() } } } class LazyModuleImp(val wrapper: LazyModule) extends MultiIOModule with LazyModuleImpLike { val (auto, dangles) = instantiate() } class LazyRawModuleImp(val wrapper: LazyModule) extends RawModule with LazyModuleImpLike { val (auto, dangles) = withClockAndReset(Bool(false).asClock, Bool(true)) { instantiate() } } class SimpleLazyModule(implicit p: Parameters) extends LazyModule { lazy val module = new LazyModuleImp(this) } trait LazyScope { this: LazyModule => def apply[T](body: => T)(implicit p: Parameters) = { val saved = LazyModule.scope LazyModule.scope = Some(this) val out = body require (LazyModule.scope.isDefined, s"LazyScope ${name} tried to exit, but scope was empty!") require (LazyModule.scope.get eq this, s"LazyScope ${name} exited before LazyModule ${LazyModule.scope.get.name} was closed") LazyModule.scope = saved out } } object LazyScope { def apply[T](name: String)(body: => T)(implicit p: Parameters) = { val scope = LazyModule(new SimpleLazyModule with LazyScope) scope.suggestName(name) scope { body } } } case class HalfEdge(serial: Int, index: Int) extends Ordered[HalfEdge] { import scala.math.Ordered.orderingToOrdered def compare(that: HalfEdge) = HalfEdge.unapply(this) compare HalfEdge.unapply(that) } case class Dangle(source: HalfEdge, sink: HalfEdge, flipped: Boolean, name: String, data: Data) final class AutoBundle(elts: (String, Data, Boolean)*) extends Record { // We need to preserve the order of elts, despite grouping by name to disambiguate things val elements = ListMap() ++ elts.zipWithIndex.map(makeElements).groupBy(_._1).values.flatMap { case Seq((key, element, i)) => Seq(i -> (key -> element)) case seq => seq.zipWithIndex.map { case ((key, element, i), j) => i -> (key + "_" + j -> element) } }.toList.sortBy(_._1).map(_._2) require (elements.size == elts.size) private def makeElements(tuple: ((String, Data, Boolean), Int)) = { val ((key, data, flip), i) = tuple // trim trailing _0_1_2 stuff so that when we append _# we don't create collisions val regex = new Regex("(_[0-9]+)*$") val element = if (flip) data.cloneType.flip else data.cloneType (regex.replaceAllIn(key, ""), element, i) } override def cloneType = (new AutoBundle(elts:_*)).asInstanceOf[this.type] }