diff --git a/.gitmodules b/.gitmodules index fa1f58748f..d99c4e5fe5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,7 @@ [submodule "dependencies/chisel"] path = dependencies/chisel url = https://github.com/chipsalliance/chisel.git +[submodule "dependencies/diplomacy"] + path = dependencies/diplomacy + url = https://github.com/chipsalliance/diplomacy.git + branch = lordspacehog/standalone_diplomacy diff --git a/build.sc b/build.sc index b19e7a8927..b17a7d69be 100644 --- a/build.sc +++ b/build.sc @@ -4,6 +4,7 @@ import mill.scalalib.publish._ import coursier.maven.MavenRepository import $file.dependencies.hardfloat.common import $file.dependencies.cde.common +import $file.dependencies.diplomacy.common import $file.dependencies.chisel.build import $file.common @@ -18,6 +19,7 @@ object v { val mainargs = ivy"com.lihaoyi::mainargs:0.5.0" val json4sJackson = ivy"org.json4s::json4s-jackson:4.0.5" val scalaReflect = ivy"org.scala-lang:scala-reflect:${scala}" + val sourcecode = ivy"com.lihaoyi::sourcecode:0.3.1" val sonatypesSnapshots = Seq( MavenRepository("https://s01.oss.sonatype.org/content/repositories/snapshots") ) @@ -79,6 +81,34 @@ trait CDE override def millSourcePath = os.pwd / "dependencies" / "cde" / "cde" } +object diplomacy extends mill.define.Cross[Diplomacy](v.chiselCrossVersions.keys.toSeq) + +trait Diplomacy + extends millbuild.dependencies.diplomacy.common.DiplomacyModule + with RocketChipPublishModule + with Cross.Module[String] { + + override def scalaVersion: T[String] = T(v.scala) + + override def millSourcePath = os.pwd / "dependencies" / "diplomacy" / "diplomacy" + + // dont use chisel from source + def chiselModule = None + def chiselPluginJar = None + + // use chisel from ivy + def chiselIvy = Some(v.chiselCrossVersions(crossValue)._1) + def chiselPluginIvy = Some(v.chiselCrossVersions(crossValue)._2) + + // use CDE from source untill published to sonatype + def cdeModule = Some(cde) + + // no cde ivy currently published + def cdeIvy = None + + def sourcecodeIvy = v.sourcecode +} + object rocketchip extends Cross[RocketChip](v.chiselCrossVersions.keys.toSeq) trait RocketChip @@ -104,6 +134,10 @@ trait RocketChip def cdeModule = cde + def diplomacyModule = diplomacy(crossValue) + + def diplomacyIvy = None + def mainargsIvy = v.mainargs def json4sJacksonIvy = v.json4sJackson @@ -127,7 +161,6 @@ trait RocketChipPublishModule override def publishVersion: T[String] = T("1.6-SNAPSHOT") } - // Tests trait Emulator extends Cross.Module2[String, String] { val top: String = crossValue diff --git a/common.sc b/common.sc index 574f0ad431..e3f8445d9c 100644 --- a/common.sc +++ b/common.sc @@ -45,16 +45,20 @@ trait RocketChipModule // should be cde/common.sc#CDEModule def cdeModule: ScalaModule + def diplomacyModule: ScalaModule + + def diplomacyIvy: Option[Dep] + def mainargsIvy: Dep def json4sJacksonIvy: Dep - override def moduleDeps = super.moduleDeps ++ Seq(macrosModule, hardfloatModule, cdeModule) + override def moduleDeps = super.moduleDeps ++ Seq(macrosModule, hardfloatModule, cdeModule, diplomacyModule) override def ivyDeps = T( super.ivyDeps() ++ Agg( mainargsIvy, - json4sJacksonIvy - ) + json4sJacksonIvy, + ) ++ diplomacyIvy ) } diff --git a/dependencies/diplomacy b/dependencies/diplomacy new file mode 160000 index 0000000000..b4f93b7747 --- /dev/null +++ b/dependencies/diplomacy @@ -0,0 +1 @@ +Subproject commit b4f93b7747e59376e65e11d83982f5b865d7e6f6 diff --git a/src/main/scala/aop/Select.scala b/src/main/scala/aop/Select.scala deleted file mode 100644 index 132b859ed1..0000000000 --- a/src/main/scala/aop/Select.scala +++ /dev/null @@ -1,121 +0,0 @@ -// See LICENSE.SiFive for license details. - -package freechips.rocketchip.aop - -import chisel3.Data -import org.chipsalliance.cde.config.Parameters -import freechips.rocketchip.diplomacy.{ - AnyMixedNode, - BaseNode, - InwardNode, - LazyModule, - MixedNode, - OutwardNode, -} - -/** Combinators for finding specific sets of [[LazyModule]]s/[[Node]]s. - * - * These can be used for e.g. finding specific TLBundles in a design and - * placing monitors or annotating metadata. - */ -object Select { - - /** Contains information about an inward edge of a node - */ - case class InwardEdge[Bundle <: Data, EdgeInParams]( - params: Parameters, - bundle: Bundle, - edge: EdgeInParams, - node: OutwardNode[_, _, Bundle], - ) - - /** Contains information about an outward edge of a node - */ - case class OutwardEdge[Bundle <: Data, EdgeOutParams]( - params: Parameters, - bundle: Bundle, - edge: EdgeOutParams, - node: InwardNode[_, _, Bundle], - ) - - /** Collects the [[InwardEdge]]s of a node. Defined as a separate method so - * that the bundle/edge types can be set properly - */ - private def getInwardEdges[BI <: Data, EI](node: MixedNode[_, _, EI, BI, _, _, _, _ <: Data]): Iterable[InwardEdge[BI, EI]] = { - node.iPorts.zip(node.in).map { - case ((_, node, params, _), (bundle, edge)) => - InwardEdge(params, bundle, edge, node) - } - } - - /** Applies the collect function to each [[InwardEdge]] of a node - */ - def collectInwardEdges[T](node: BaseNode)(collect: PartialFunction[InwardEdge[_ <: Data, _], T]): Iterable[T] = { - node match { - case node: AnyMixedNode => getInwardEdges(node).collect(collect) - case _ => Seq.empty - } - } - - /** Collects the [[OutwardEdge]]s of a node. Defined as a separate method so - * that the bundle/edge types can be set properly - */ - private def getOutwardEdges[BO <: Data, EO](node: MixedNode[_, _, _, _ <: Data, _, _, EO, BO]): Iterable[OutwardEdge[BO, EO]] = { - node.oPorts.zip(node.out).map { - case ((_, node, params, _), (bundle, edge)) => - OutwardEdge(params, bundle, edge, node) - } - } - - /** Applies the collect function to each [[OutardEdge]] of a node - */ - def collectOutwardEdges[T](node: BaseNode)(collect: PartialFunction[OutwardEdge[_ <: Data, _], T]): Iterable[T] = { - node match { - case node: AnyMixedNode => getOutwardEdges(node).collect(collect) - case _ => Seq.empty - } - } - - /** Applies the collect function to a [[LazyModule]] and recursively to all - * of its children. - */ - def collectDeep[T](lmod: LazyModule)(collect: PartialFunction[LazyModule, T]): Iterable[T] = { - collect.lift(lmod) ++ - lmod.getChildren.flatMap { child => - collectDeep(child)(collect) - } - } - - /** Applies the collect function to a [[LazyModule]] and its children if the - * filter function returns true. Stops recursing when the filter function - * returns false. e.g. - * for this hierarchy - * A - * / \ - * B C - * / \ \ - * D E F - * - * the following select function - * {{{ - * filterCollectDeep(A) { - * case B => false - * case _ => true - * } { m => - * printl(m) - * } - * }}} - * - * will only print modules A, C, and F - */ - def filterCollectDeep[T](lmod: LazyModule)(filter: LazyModule => Boolean)(collect: PartialFunction[LazyModule, T]): Iterable[T] = { - if (filter(lmod)) { - collect.lift(lmod) ++ - lmod.getChildren.flatMap { child => - filterCollectDeep(child)(filter)(collect) - } - } else { - Iterable.empty - } - } -} diff --git a/src/main/scala/aop/package.scala b/src/main/scala/aop/package.scala new file mode 100644 index 0000000000..a773e15355 --- /dev/null +++ b/src/main/scala/aop/package.scala @@ -0,0 +1,6 @@ +package freechips.rocketchip + +object aop { + @deprecated("aop has moved to the standalone diplomacy library.", "rocketchip 2.0.0") + val Select = _root_.org.chipsalliance.diplomacy.aop.Select +} diff --git a/src/main/scala/diplomacy/BundleBridge.scala b/src/main/scala/diplomacy/BundleBridge.scala deleted file mode 100644 index 39ba091ac9..0000000000 --- a/src/main/scala/diplomacy/BundleBridge.scala +++ /dev/null @@ -1,206 +0,0 @@ -// See LICENSE.SiFive for license details. - -package freechips.rocketchip.diplomacy - -import chisel3._ -import chisel3.experimental.SourceInfo -import chisel3.reflect.DataMirror -import chisel3.reflect.DataMirror.internal.chiselTypeClone -import org.chipsalliance.cde.config.Parameters -import freechips.rocketchip.util.DataToAugmentedData - -case class BundleBridgeParams[T <: Data](genOpt: Option[() => T]) - -case object BundleBridgeParams { - def apply[T <: Data](gen: () => T): BundleBridgeParams[T] = BundleBridgeParams(Some(gen)) -} - -case class BundleBridgeEdgeParams[T <: Data](source: BundleBridgeParams[T], sink: BundleBridgeParams[T]) - -class BundleBridgeImp[T <: Data]() extends SimpleNodeImp[BundleBridgeParams[T], BundleBridgeParams[T], BundleBridgeEdgeParams[T], T] -{ - def edge(pd: BundleBridgeParams[T], pu: BundleBridgeParams[T], p: Parameters, sourceInfo: SourceInfo) = BundleBridgeEdgeParams(pd, pu) - def bundle(e: BundleBridgeEdgeParams[T]): T = { - val sourceOpt = e.source.genOpt.map(_()) - val sinkOpt = e.sink.genOpt.map(_()) - (sourceOpt, sinkOpt) match { - case (None, None) => - throw new Exception("BundleBridge needs source or sink to provide bundle generator function") - case (Some(a), None) => chiselTypeClone(a) - case (None, Some(b)) => chiselTypeClone(b) - case (Some(a), Some(b)) => { - require(DataMirror.checkTypeEquivalence(a, b), - s"BundleBridge requires doubly-specified source and sink generators to have equivalent Chisel Data types, but got \n$a\n vs\n$b") - chiselTypeClone(a) - } - } - } - def render(e: BundleBridgeEdgeParams[T]) = RenderedEdge(colour = "#cccc00" /* yellow */) -} - -case class BundleBridgeSink[T <: Data](genOpt: Option[() => T] = None) - (implicit valName: ValName) - extends SinkNode(new BundleBridgeImp[T])(Seq(BundleBridgeParams(genOpt))) -{ - def bundle: T = in(0)._1 - - private def inferOutput = bundle.getElements.forall { elt => - DataMirror.directionOf(elt) == ActualDirection.Unspecified - } - - def makeIO()(implicit valName: ValName): T = { - val io: T = IO(if (inferOutput) Output(chiselTypeOf(bundle)) else chiselTypeClone(bundle)) - io.suggestName(valName.name) - io <> bundle - io - } - def makeIO(name: String): T = makeIO()(ValName(name)) -} - -object BundleBridgeSink { - def apply[T <: Data]()(implicit valName: ValName): BundleBridgeSink[T] = { - BundleBridgeSink(None) - } -} - -case class BundleBridgeSource[T <: Data](genOpt: Option[() => T] = None)(implicit valName: ValName) extends SourceNode(new BundleBridgeImp[T])(Seq(BundleBridgeParams(genOpt))) -{ - def bundle: T = out(0)._1 - - private def inferInput = bundle.getElements.forall { elt => - DataMirror.directionOf(elt) == ActualDirection.Unspecified - } - - def makeIO()(implicit valName: ValName): T = { - val io: T = IO(if (inferInput) Input(chiselTypeOf(bundle)) else Flipped(chiselTypeClone(bundle))) - io.suggestName(valName.name) - bundle <> io - io - } - def makeIO(name: String): T = makeIO()(ValName(name)) - - private var doneSink = false - def makeSink()(implicit p: Parameters) = { - require (!doneSink, "Can only call makeSink() once") - doneSink = true - val sink = BundleBridgeSink[T]() - sink := this - sink - } -} - -object BundleBridgeSource { - def apply[T <: Data]()(implicit valName: ValName): BundleBridgeSource[T] = { - BundleBridgeSource(None) - } - def apply[T <: Data](gen: () => T)(implicit valName: ValName): BundleBridgeSource[T] = { - BundleBridgeSource(Some(gen)) - } -} - -case class BundleBridgeIdentityNode[T <: Data]()(implicit valName: ValName) extends IdentityNode(new BundleBridgeImp[T])() -case class BundleBridgeEphemeralNode[T <: Data]()(implicit valName: ValName) extends EphemeralNode(new BundleBridgeImp[T])() - -object BundleBridgeNameNode { - def apply[T <: Data](name: String) = BundleBridgeIdentityNode[T]()(ValName(name)) -} - -case class BundleBridgeNexusNode[T <: Data](default: Option[() => T] = None, - inputRequiresOutput: Boolean = false) // when false, connecting a source does not mandate connecting a sink - (implicit valName: ValName) - extends NexusNode(new BundleBridgeImp[T])( - dFn = seq => seq.headOption.getOrElse(BundleBridgeParams(default)), - uFn = seq => seq.headOption.getOrElse(BundleBridgeParams(None)), - inputRequiresOutput = inputRequiresOutput, - outputRequiresInput = !default.isDefined) - -class BundleBridgeNexus[T <: Data]( - inputFn: Seq[T] => T, - outputFn: (T, Int) => Seq[T], - default: Option[() => T] = None, - inputRequiresOutput: Boolean = false, - override val shouldBeInlined: Boolean = true -) (implicit p: Parameters) extends LazyModule -{ - val node = BundleBridgeNexusNode[T](default, inputRequiresOutput) - - lazy val module = new Impl - class Impl extends LazyRawModuleImp(this) { - val defaultWireOpt = default.map(_()) - val inputs: Seq[T] = node.in.map(_._1) - inputs.foreach { i => require(DataMirror.checkTypeEquivalence(i, inputs.head), - s"${node.context} requires all inputs have equivalent Chisel Data types, but got\n$i\nvs\n${inputs.head}") - } - inputs.flatMap(_.getElements).foreach { elt => DataMirror.directionOf(elt) match { - case ActualDirection.Output => () - case ActualDirection.Unspecified => () - case _ => require(false, s"${node.context} can only be used with Output-directed Bundles") - } } - - val outputs: Seq[T] = if (node.out.size > 0) { - val broadcast: T = if (inputs.size >= 1) inputFn(inputs) else defaultWireOpt.get - outputFn(broadcast, node.out.size) - } else { Nil } - - node.out.map(_._1).foreach { o => require(DataMirror.checkTypeEquivalence(o, outputs.head), - s"${node.context} requires all outputs have equivalent Chisel Data types, but got\n$o\nvs\n${outputs.head}") - } - - require(outputs.size == node.out.size, - s"${node.context} outputFn must generate one output wire per edgeOut, but got ${outputs.size} vs ${node.out.size}") - - node.out.zip(outputs).foreach { case ((out, _), bcast) => out := bcast } - } -} - -object BundleBridgeNexus { - def safeRegNext[T <: Data](x: T): T = { - val reg = Reg(chiselTypeOf(x)) - reg := x - reg - } - - def requireOne[T <: Data](registered: Boolean)(seq: Seq[T]): T = { - require(seq.size == 1, "BundleBroadcast default requires one input") - if (registered) safeRegNext(seq.head) else seq.head - } - - def orReduction[T <: Data](registered: Boolean)(seq: Seq[T]): T = { - val x = seq.reduce((a,b) => (a.asUInt | b.asUInt).asTypeOf(seq.head)) - if (registered) safeRegNext(x) else x - } - - def fillN[T <: Data](registered: Boolean)(x: T, n: Int): Seq[T] = Seq.fill(n) { - if (registered) safeRegNext(x) else x - } - - def apply[T <: Data]( - inputFn: Seq[T] => T = orReduction[T](false) _, - outputFn: (T, Int) => Seq[T] = fillN[T](false) _, - default: Option[() => T] = None, - inputRequiresOutput: Boolean = false, - shouldBeInlined: Boolean = true - )(implicit p: Parameters): BundleBridgeNexusNode[T] = { - val nexus = LazyModule(new BundleBridgeNexus[T](inputFn, outputFn, default, inputRequiresOutput, shouldBeInlined)) - nexus.node - } -} - -object BundleBroadcast { - def apply[T <: Data]( - name: Option[String] = None, - registered: Boolean = false, - default: Option[() => T] = None, - inputRequiresOutput: Boolean = false, // when false, connecting a source does not mandate connecting a sink - shouldBeInlined: Boolean = true - )(implicit p: Parameters): BundleBridgeNexusNode[T] = { - val broadcast = LazyModule(new BundleBridgeNexus[T]( - inputFn = BundleBridgeNexus.requireOne[T](registered) _, - outputFn = BundleBridgeNexus.fillN[T](registered) _, - default = default, - inputRequiresOutput = inputRequiresOutput, - shouldBeInlined = shouldBeInlined)) - name.foreach(broadcast.suggestName(_)) - broadcast.node - } -} diff --git a/src/main/scala/diplomacy/Clone.scala b/src/main/scala/diplomacy/Clone.scala deleted file mode 100644 index 4e0b9209d6..0000000000 --- a/src/main/scala/diplomacy/Clone.scala +++ /dev/null @@ -1,37 +0,0 @@ -// See LICENSE.SiFive for license details. - -package freechips.rocketchip.diplomacy - -import chisel3._ -import chisel3.experimental.{CloneModuleAsRecord, SourceInfo} - -final class CloneLazyModule private (val base: LazyModule) -{ - // Pay special attention to the .iParams and .oParams of the node, which - // indicate the parameters a stand-in master must supply. - def clone[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data](node: NodeHandle[DI, UI, EI, BI, DO, UO, EO, BO])(implicit valName: ValName) = - new MixedTestNode(node, this) - - protected[diplomacy] lazy val io = CloneModuleAsRecord(base.module) -} - -object CloneLazyModule -{ - /** The old API **/ - def apply(base: LazyModule) = new CloneLazyModule(base) - - - /** Constructs a [[LazyModule]], but replaces its [[LazyModuleImp]] with a cloned [[LazyModuleImp]] - * from another source. The user of [[CloneLazyModule]] must be careful to guarantee that - * bc and cloneProto have equivalent [[LazyModuleImp]]'s. - * - * @param bc [[LazyModule]] instance to wrap, this instance will not evaluate its own [[LazyModuleImp]] - * @param cloneProto [[LazyModule]] instance which will provide the [[LazyModuleImp]] implementation for bc - */ - def apply[A <: LazyModule, B <: LazyModule](bc: A, cloneProto: B)(implicit valName: ValName, sourceInfo: SourceInfo): A = { - require(LazyModule.scope.isDefined, s"CloneLazyModule ${bc.name} ${sourceLine(sourceInfo)} can only exist as the child of a parent LazyModule") - LazyModule(bc) - bc.cloneProto = Some(cloneProto) - bc - } -} diff --git a/src/main/scala/diplomacy/LazyModule.scala b/src/main/scala/diplomacy/LazyModule.scala deleted file mode 100644 index ff6e037876..0000000000 --- a/src/main/scala/diplomacy/LazyModule.scala +++ /dev/null @@ -1,612 +0,0 @@ -// See LICENSE.SiFive for license details. - -package freechips.rocketchip.diplomacy - -import chisel3._ -import chisel3.{Module, RawModule, Reset, withClockAndReset} -import chisel3.experimental.{ChiselAnnotation, CloneModuleAsRecord, SourceInfo, UnlocatableSourceInfo} -import firrtl.passes.InlineAnnotation -import org.chipsalliance.cde.config.Parameters - -import scala.collection.immutable.{SeqMap, SortedMap} -import scala.util.matching._ - -/** While the [[freechips.rocketchip.diplomacy]] package allows fairly abstract parameter negotiation while constructing a DAG, - * [[LazyModule]] builds on top of the DAG annotated with the negotiated parameters and leverage's Scala's lazy evaluation property to split Chisel module generation into two phases: - * - * - Phase 1 (diplomatic) states parameters, hierarchy, and connections: - * - [[LazyModule]] and [[BaseNode]] instantiation. - * - [[BaseNode]] binding. - * - Phase 2 (lazy) generates [[chisel3]] Modules: - * - Parameters are negotiated across [[BaseNode]]s. - * - Concrete [[Bundle]]s are created along [[BaseNode]]s and connected - * - [[AutoBundle]] are automatically connected along [[Edges]], punching IO as necessary though module hierarchy - * - [[LazyModuleImpLike]] generates [[chisel3.Module]]s. - */ -abstract class LazyModule()(implicit val p: Parameters) { - /** Contains sub-[[LazyModule]]s; can be accessed by [[getChildren]]. */ - protected[diplomacy] var children: List[LazyModule] = List[LazyModule]() - /** Contains the [[BaseNode]]s instantiated within this instance. */ - protected[diplomacy] var nodes: List[BaseNode] = List[BaseNode]() - /** Stores [[SourceInfo]] of this instance. - * - * The companion object factory method will set this to the correct value. - */ - protected[diplomacy] var info: SourceInfo = UnlocatableSourceInfo - /** Parent of this LazyModule. If this instance is at the top of the hierarchy, this will be [[None]]. */ - protected[diplomacy] val parent: Option[LazyModule] = LazyModule.scope - /** If set, the LazyModule this LazyModule will be a clone of - * Note that children of a cloned module will also have this set - */ - protected[diplomacy] var cloneProto: Option[LazyModule] = None - - /** Code snippets from [[InModuleBody]] injection. */ - protected[diplomacy] var inModuleBody: List[() => Unit] = List[() => Unit]() - - /** Sequence of ancestor LazyModules, starting with [[parent]]. */ - def parents: Seq[LazyModule] = parent match { - case None => Nil - case Some(x) => x +: x.parents - } - - // Push this instance onto the [[LazyModule.scope]] stack. - LazyModule.scope = Some(this) - parent.foreach(p => p.children = this :: p.children) - - /** Accumulates Some(names), taking the final one. `None`s are ignored. */ - private var suggestedNameVar: Option[String] = None - - /** Suggests instance name for [[LazyModuleImpLike]] module. */ - def suggestName(x: String): this.type = suggestName(Some(x)) - - def suggestName(x: Option[String]): this.type = { - x.foreach { n => suggestedNameVar = Some(n) } - this - } - - /** Finds the name of the first non-anonymous Scala class while walking up the class hierarchy. */ - private def findClassName(c: Class[_]): String = { - val n = c.getName.split('.').last - if (n.contains('$')) findClassName(c.getSuperclass) else n - } - - /** Scala class name of this instance. */ - lazy val className: String = findClassName(getClass) - /** Suggested instance name. Defaults to [[className]].*/ - lazy val suggestedName: String = suggestedNameVar.getOrElse(className) - /** Suggested module name. Defaults to [[className]].*/ - lazy val desiredName: String = className // + hashcode? - - /** Return instance name. */ - def name: String = suggestedName // className + suggestedName ++ hashcode ? - /** Return source line that defines this instance. */ - def line: String = sourceLine(info) - - // Accessing these names can only be done after circuit elaboration! - /** Module name in verilog, used in GraphML. - * For cloned lazyModules, this is the name of the prototype - */ - lazy val moduleName: String = cloneProto.map(_.module.name).getOrElse(module.name) - /** Hierarchical path of this instance, used in GraphML. - * For cloned modules, construct this manually (since this.module should not be evaluated) - */ - lazy val pathName: String = cloneProto.map(p => s"${parent.get.pathName}.${p.instanceName}") - .getOrElse(module.pathName) - - /** Instance name in verilog. Should only be accessed after circuit elaboration. */ - lazy val instanceName: String = pathName.split('.').last - - /** [[chisel3]] hardware implementation of this [[LazyModule]]. - * - * Subclasses should define this function as `lazy val`s for lazy evaluation. - * Generally, the evaluation of this marks the beginning of phase 2. - */ - def module: LazyModuleImpLike - - /** Recursively traverse all child LazyModules and Nodes of this LazyModule - * to construct the set of empty [[Dangle]]'s that are this module's top-level IO - * This is effectively doing the same thing as [[LazyModuleImp.instantiate]], but - * without constructing any [[Module]]'s - */ - protected[diplomacy] def cloneDangles(): List[Dangle] = { - children.foreach(c => require(c.cloneProto.isDefined, s"${c.info}, ${c.parent.get.info}")) - val childDangles = children.reverse.flatMap { c => c.cloneDangles() } - val nodeDangles = nodes.reverse.flatMap(n => n.cloneDangles()) - 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) - a.source - case _ => - None - } - val forward = allDangles.filter(d => !done(d.source)) - val dangles = forward.map { d => - d.copy(name = suggestedName + "_" + d.name) - } - dangles - } - - /** Whether to omit generating the GraphML for this [[LazyModule]]. - * - * Recursively checks whether all [[BaseNode]]s and children [[LazyModule]]s should omit GraphML - * generation. - */ - def omitGraphML: Boolean = nodes.forall(_.omitGraphML) && children.forall(_.omitGraphML) - - /** Whether this [[LazyModule]]'s module should be marked for in-lining by FIRRTL. - * - * The default heuristic is to inline any parents whose children have been inlined - * and whose nodes all produce identity circuits. - */ - def shouldBeInlined: Boolean = nodes.forall(_.circuitIdentity) && children.forall(_.shouldBeInlined) - - /** GraphML representation for this instance. - * - * This is a representation of the Nodes, Edges, LazyModule hierarchy, - * and any other information that is added in by implementations. - * It can be converted to an image with various third-party tools. - */ - 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 - } - - /** A globally unique [[LazyModule]] index for this instance. */ - private val index = { - LazyModule.index = LazyModule.index + 1 - LazyModule.index - } - - /** Generate GraphML fragment for nodes. - * - * @param buf String buffer to write to. - * @param pad Padding as prefix for indentation purposes. - */ - private def nodesGraphML(buf: StringBuilder, pad: String): Unit = { - 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.formatNode}, \n${n.nodedebugstring}\n""" - buf ++= s"""$pad \n""" - } - children.filter(!_.omitGraphML).foreach(_.nodesGraphML(buf, pad + " ")) - buf ++= s"""$pad \n""" - buf ++= s"""$pad\n""" - } - - /** Generate GraphML fragment for edges. - * - * @param buf String buffer to write to. - * @param pad Padding as prefix for indentation purposes. - */ - private def edgesGraphML(buf: StringBuilder, pad: String): Unit = { - 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) } - } - - /** Call function on all of this [[LazyModule]]'s [[children]]. - * - * @param iterfunc Function to call on each descendant. - */ - def childrenIterator(iterfunc: LazyModule => Unit): Unit = { - iterfunc(this) - children.foreach(_.childrenIterator(iterfunc)) - } - - /** Call function on all of this [[LazyModule]]'s [[nodes]]. - * - * @param iterfunc Function to call on each descendant. - */ - def nodeIterator(iterfunc: BaseNode => Unit): Unit = { - nodes.foreach(iterfunc) - childrenIterator(_.nodes.foreach(iterfunc)) - } - - /** Accessor for [[children]]. */ - def getChildren: List[LazyModule] = children - - /** Accessor for [[nodes]]. */ - def getNodes: List[BaseNode] = nodes -} - -object LazyModule { - /** Current [[LazyModule]] scope. The scope is a stack of [[LazyModule]]/[[LazyScope]]s. - * - * Each call to [[LazyScope.apply]] or [[LazyModule.apply]] will push that item onto the current scope. - */ - protected[diplomacy] var scope: Option[LazyModule] = None - /** Global index of [[LazyModule]]. Note that there is no zeroth module. */ - private var index = 0 - - /** Wraps a [[LazyModule]], handling bookkeeping of scopes. - * - * This method manages the scope and index of the [[LazyModule]]s. All [[LazyModule]]s must be - * wrapped exactly once. - * - * @param bc [[LazyModule]] instance to be wrapped. - * @param valName [[ValName]] used to name this instance, - * it can be automatically generated by [[ValName]] macro, or specified manually. - * @param sourceInfo [[SourceInfo]] information about where this [[LazyModule]] is being generated - */ - def apply[T <: LazyModule](bc: T)(implicit valName: ValName, sourceInfo: SourceInfo): T = { - // Make sure the user puts [[LazyModule]] around modules in the correct order. - require(scope.isDefined, s"LazyModule() applied to ${bc.name} twice ${sourceLine(sourceInfo)}. Ensure that descendant LazyModules are instantiated with the LazyModule() wrapper and that you did not call LazyModule() twice.") - require(scope.get eq bc, s"LazyModule() applied to ${bc.name} before ${scope.get.name} ${sourceLine(sourceInfo)}") - // Pop from the [[LazyModule.scope]] stack. - scope = bc.parent - bc.info = sourceInfo - if (bc.suggestedNameVar.isEmpty) bc.suggestName(valName.name) - bc - } -} - -/** Trait describing the actual [[Module]] implementation wrapped by a [[LazyModule]]. - * - * This is the actual Chisel module that is lazily-evaluated in the second phase of Diplomacy. - */ -sealed trait LazyModuleImpLike extends RawModule { - /** [[LazyModule]] that contains this instance. */ - val wrapper: LazyModule - /** IOs that will be automatically "punched" for this instance. */ - val auto: AutoBundle - /** The metadata that describes the [[HalfEdge]]s which generated [[auto]]. */ - protected[diplomacy] val dangles: Seq[Dangle] - - // [[wrapper.module]] had better not be accessed while LazyModules are still being built! - require(LazyModule.scope.isEmpty, s"${wrapper.name}.module was constructed before LazyModule() was run on ${LazyModule.scope.get.name}") - - /** Set module name. Defaults to the containing LazyModule's desiredName.*/ - override def desiredName: String = wrapper.desiredName - - suggestName(wrapper.suggestedName) - - /** [[Parameters]] for chisel [[Module]]s. */ - implicit val p: Parameters = wrapper.p - - /** instantiate this [[LazyModule]], - * return [[AutoBundle]] and a unconnected [[Dangle]]s from this module and submodules. */ - protected[diplomacy] def instantiate(): (AutoBundle, List[Dangle]) = { - // 1. It will recursively append [[wrapper.children]] into [[chisel3.internal.Builder]], - // 2. return [[Dangle]]s from each module. - val childDangles = wrapper.children.reverse.flatMap { c => - implicit val sourceInfo: SourceInfo = c.info - c.cloneProto.map { cp => - // If the child is a clone, then recursively set cloneProto of its children as well - def assignCloneProtos(bases: Seq[LazyModule], clones: Seq[LazyModule]): Unit = { - require(bases.size == clones.size) - (bases zip clones).map { case (l,r) => - require(l.getClass == r.getClass, s"Cloned children class mismatch ${l.name} != ${r.name}") - l.cloneProto = Some(r) - assignCloneProtos(l.children, r.children) - } - } - assignCloneProtos(c.children, cp.children) - // Clone the child module as a record, and get its [[AutoBundle]] - val clone = CloneModuleAsRecord(cp.module).suggestName(c.suggestedName) - val clonedAuto = clone("auto").asInstanceOf[AutoBundle] - // Get the empty [[Dangle]]'s of the cloned child - val rawDangles = c.cloneDangles() - require(rawDangles.size == clonedAuto.elements.size) - // Assign the [[AutoBundle]] fields of the cloned record to the empty [[Dangle]]'s - val dangles = (rawDangles zip clonedAuto.elements).map { case (d, (_, io)) => - d.copy(dataOpt = Some(io)) - } - dangles - } .getOrElse { - // For non-clones, instantiate the child module - val mod = Module(c.module) - mod.dangles - } - } - - // Ask each node in this [[LazyModule]] to call [[BaseNode.instantiate]]. - // This will result in a sequence of [[Dangle]] from these [[BaseNode]]s. - val nodeDangles = wrapper.nodes.reverse.flatMap(_.instantiate()) - // Accumulate all the [[Dangle]]s from this node and any accumulated from its [[wrapper.children]] - val allDangles = nodeDangles ++ childDangles - // Group [[allDangles]] by their [[source]]. - val pairing = SortedMap(allDangles.groupBy(_.source).toSeq: _*) - // For each [[source]] set of [[Dangle]]s of size 2, ensure that these - // can be connected as a source-sink pair (have opposite flipped value). - // Make the connection and mark them as [[done]]. - val done = Set() ++ pairing.values.filter(_.size == 2).map { - case Seq(a, b) => - require(a.flipped != b.flipped) - // @todo <> in chisel3 makes directionless connection. - if (a.flipped) { - a.data <> b.data - } else { - b.data <> a.data - } - a.source - case _ => - None - } - // Find all [[Dangle]]s which are still not connected. These will end up as [[AutoBundle]] [[IO]] ports on the module. - val forward = allDangles.filter(d => !done(d.source)) - // Generate [[AutoBundle]] IO from [[forward]]. - val auto = IO(new AutoBundle(forward.map { d => (d.name, d.data, d.flipped) }: _*)) - // Pass the [[Dangle]]s which remained and were used to generate the [[AutoBundle]] I/O ports up to the [[parent]] [[LazyModule]] - val dangles = (forward zip auto.elements) map { case (d, (_, io)) => - if (d.flipped) { - d.data <> io - } else { - io <> d.data - } - d.copy(dataOpt = Some(io), name = wrapper.suggestedName + "_" + d.name) - } - // Push all [[LazyModule.inModuleBody]] to [[chisel3.internal.Builder]]. - wrapper.inModuleBody.reverse.foreach { - _ () - } - - if (wrapper.shouldBeInlined) { - chisel3.experimental.annotate(new ChiselAnnotation { - def toFirrtl = InlineAnnotation(toNamed) - }) - } - - // Return [[IO]] and [[Dangle]] of this [[LazyModuleImp]]. - (auto, dangles) - } -} - -/** Actual description of a [[Module]] which can be instantiated by a call to [[LazyModule.module]]. - * - * @param wrapper the [[LazyModule]] from which the `.module` call is being made. - */ -class LazyModuleImp(val wrapper: LazyModule) extends Module with LazyModuleImpLike { - /** Instantiate hardware of this `Module`. */ - val (auto, dangles) = instantiate() -} - -/** Actual description of a [[RawModule]] which can be instantiated by a call to [[LazyModule.module]]. - * - * @param wrapper the [[LazyModule]] from which the `.module` call is being made. - */ -class LazyRawModuleImp(val wrapper: LazyModule) extends RawModule with LazyModuleImpLike { - // These wires are the default clock+reset for all LazyModule children. - // It is recommended to drive these even if you manually drive the [[clock]] and [[reset]] of all of the - // [[LazyRawModuleImp]] children. - // Otherwise, anonymous children ([[Monitor]]s for example) will not have their [[clock]] and/or [[reset]] driven properly. - /** drive clock explicitly. */ - val childClock: Clock = Wire(Clock()) - /** drive reset explicitly. */ - val childReset: Reset = Wire(Reset()) - // the default is that these are disabled - childClock := false.B.asClock - childReset := chisel3.DontCare - - def provideImplicitClockToLazyChildren: Boolean = false - val (auto, dangles) = if (provideImplicitClockToLazyChildren) { - withClockAndReset(childClock, childReset) { instantiate() } - } else { - instantiate() - } -} - -/** Used for a [[LazyModule]] which does not need to define any [[LazyModuleImp]] implementation. - * - * It can be used as wrapper that only instantiates and connects [[LazyModule]]s. - */ -class SimpleLazyModule(implicit p: Parameters) extends LazyModule { - lazy val module = new LazyModuleImp(this) -} -class SimpleLazyRawModule(implicit p: Parameters) extends LazyModule { - lazy val module = new LazyRawModuleImp(this) -} - - -/** Allows dynamic creation of [[Module]] hierarchy and "shoving" logic into a [[LazyModule]]. */ -trait LazyScope { - this: LazyModule => - override def toString: String = s"LazyScope named $name" - - /** Evaluate `body` in the current [[LazyModule.scope]] */ - def apply[T](body: => T): T = { - // Preserve the previous value of the [[LazyModule.scope]], because when calling [[apply]] function, - // [[LazyModule.scope]] will be altered. - val saved = LazyModule.scope - // [[LazyModule.scope]] stack push. - LazyModule.scope = Some(this) - // Evaluate [[body]] in the current `scope`, saving the result to [[out]]. - val out = body - // Check that the `scope` after evaluating `body` is the same as when we started. - 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]] stack pop. - LazyModule.scope = saved - out - } -} - -/** Used to automatically create a level of module hierarchy (a [[SimpleLazyModule]]) within which [[LazyModule]]s can be instantiated and connected. - * - * It will instantiate a [[SimpleLazyModule]] to manage evaluation of `body` and evaluate `body` code snippets in this scope. - */ -object LazyScope { - /** Create a [[LazyScope]] with an implicit instance name. - * - * @param body code executed within the generated [[SimpleLazyModule]]. - * @param valName instance name of generated [[SimpleLazyModule]]. - * @param p [[Parameters]] propagated to [[SimpleLazyModule]]. - */ - def apply[T](body: => T)(implicit valName: ValName, p: Parameters): T = { - apply(valName.name, "SimpleLazyModule", None)(body)(p) - } - - /** Create a [[LazyScope]] with an explicitly defined instance name. - * - * @param name instance name of generated [[SimpleLazyModule]]. - * @param body code executed within the generated `SimpleLazyModule` - * @param p [[Parameters]] propagated to [[SimpleLazyModule]]. - */ - def apply[T](name: String)(body: => T)(implicit p: Parameters): T = { - apply(name, "SimpleLazyModule", None)(body)(p) - } - - /** Create a [[LazyScope]] with an explicit instance and class name, and control inlining. - * - * @param name instance name of generated [[SimpleLazyModule]]. - * @param desiredModuleName class name of generated [[SimpleLazyModule]]. - * @param overrideInlining tell FIRRTL that this [[SimpleLazyModule]]'s module should be inlined. - * @param body code executed within the generated `SimpleLazyModule` - * @param p [[Parameters]] propagated to [[SimpleLazyModule]]. - */ - def apply[T]( - name: String, - desiredModuleName: String, - overrideInlining: Option[Boolean] = None) - (body: => T) - (implicit p: Parameters): T = - { - val scope = LazyModule(new SimpleLazyModule with LazyScope { - override lazy val desiredName = desiredModuleName - override def shouldBeInlined = overrideInlining.getOrElse(super.shouldBeInlined) - }).suggestName(name) - scope { - body - } - } - - /** Create a [[LazyScope]] to temporarily group children for some reason, but tell Firrtl to inline it. - * - * For example, we might want to control a set of children's clocks but then not keep the parent wrapper. - * - * @param body code executed within the generated `SimpleLazyModule` - * @param p [[Parameters]] propagated to [[SimpleLazyModule]]. - */ - def inline[T](body: => T)(implicit p: Parameters): T = { - apply("noname", "ShouldBeInlined", Some(false))(body)(p) - } -} - -/** One side metadata of a [[Dangle]]. - * - * Describes one side of an edge going into or out of a [[BaseNode]]. - * - * @param serial the global [[BaseNode.serial]] number of the [[BaseNode]] that this [[HalfEdge]] connects to. - * @param index the `index` in the [[BaseNode]]'s input or output port list that this [[HalfEdge]] belongs to. - */ -case class HalfEdge(serial: Int, index: Int) extends Ordered[HalfEdge] { - - import scala.math.Ordered.orderingToOrdered - - def compare(that: HalfEdge): Int = HalfEdge.unapply(this) compare HalfEdge.unapply(that) -} - -/** [[Dangle]] captures the `IO` information of a [[LazyModule]] and which two [[BaseNode]]s the [[Edges]]/[[Bundle]] connects. - * - * [[Dangle]]s are generated by [[BaseNode.instantiate]] - * using [[MixedNode.danglesOut]] and [[MixedNode.danglesIn]] , - * [[LazyModuleImp.instantiate]] connects those that go to internal or explicit IO connections - * in a [[LazyModule]]. - * - * @param source the source [[HalfEdge]] of this [[Dangle]], which captures the source [[BaseNode]] and the port `index` within that [[BaseNode]]. - * @param sink sink [[HalfEdge]] of this [[Dangle]], which captures the sink [[BaseNode]] and the port `index` within that [[BaseNode]]. - * @param flipped flip or not in [[AutoBundle.makeElements]]. If true this corresponds to `danglesOut`, if false it corresponds to `danglesIn`. - * @param dataOpt actual [[Data]] for the hardware connection. Can be empty if this belongs to a cloned module - */ -case class Dangle(source: HalfEdge, sink: HalfEdge, flipped: Boolean, name: String, dataOpt: Option[Data]) { - def data = dataOpt.get -} - -/** [[AutoBundle]] will construct the [[Bundle]]s for a [[LazyModule]] in [[LazyModuleImpLike.instantiate]], - * - * @param elts is a sequence of data containing for each IO port a tuple of (name, data, flipped), where - * name: IO name - * data: actual data for connection. - * flipped: flip or not in [[makeElements]] - */ -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: SeqMap[String, Data] = SeqMap() ++ elts.zipWithIndex.map(makeElements).groupBy(_._1).values.flatMap { - // If name is unique, it will return a Seq[index -> (name -> data)]. - case Seq((key, element, i)) => Seq(i -> (key -> element)) - // If name is not unique, name will append with j, and return `Seq[index -> (s"${name}_${j}" -> data)]`. - case seq => seq.zipWithIndex.map { case ((key, element, i), j) => i -> (key + "_" + j -> element) } - }.toList.sortBy(_._1).map(_._2) - require(elements.size == elts.size) - - // Trim final "(_[0-9]+)*$" in the name, flip data with flipped. - 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) Flipped(data.cloneType) else data.cloneType - (regex.replaceAllIn(key, ""), element, i) - } -} - -trait ModuleValue[T] { - def getWrappedValue: T -} - -/** Used to inject code snippets to be evaluated in [[LazyModuleImp.instantiate]] in the current [[LazyModule.scope]]. - * - * It can be used to create additional hardware outside of the [[LazyModule.children]], - * connections other than the internal [[BaseNode]] connections, - * or additional IOs aside from the [[AutoBundle]] - */ -object InModuleBody { - def apply[T](body: => T): ModuleValue[T] = { - require(LazyModule.scope.isDefined, s"InModuleBody invoked outside a LazyModule") - val scope = LazyModule.scope.get - // a wrapper to [[body]], being able to extract result after `execute`. - val out = new ModuleValue[T] { - var result: Option[T] = None - - def execute(): Unit = { - result = Some(body) - } - - def getWrappedValue: T = { - require(result.isDefined, s"InModuleBody contents were requested before module was evaluated!") - result.get - } - } - - // Prepend [[out.execute]] to [[scope.inModuleBody]], - // it is a val with type of `() => Unit`, which will be executed in [[LazyModuleImp.instantiate]]. - scope.inModuleBody = out.execute _ +: scope.inModuleBody - out - } -} diff --git a/src/main/scala/diplomacy/Nodes.scala b/src/main/scala/diplomacy/Nodes.scala deleted file mode 100644 index 51d3b7e92a..0000000000 --- a/src/main/scala/diplomacy/Nodes.scala +++ /dev/null @@ -1,1800 +0,0 @@ -// See LICENSE.SiFive for license details. - -package freechips.rocketchip.diplomacy - -import chisel3._ -import chisel3.experimental.SourceInfo -import org.chipsalliance.cde.config.{Field, Parameters} -import freechips.rocketchip.util.HeterogeneousBag - -import scala.collection.immutable -import scala.collection.mutable.ListBuffer - -/** A field available in [[Parameters]] used to determine whether [[InwardNodeImp.monitor]] will be called. */ -case object MonitorsEnabled extends Field[Boolean](true) - -/** When rendering the edge in a graphical format, flip the order in which the edges' source and sink are presented. - * - * For example, when rendering graphML, yEd by default tries to put the source node vertically above the sink node, but - * [[RenderFlipped]] inverts this relationship. When a particular [[LazyModule]] contains both source nodes and sink nodes, - * flipping the rendering of one node's edge will usual produce a more concise visual layout for the [[LazyModule]]. - */ -case object RenderFlipped extends Field[Boolean](false) - -/** [[RenderedEdge]] can set the color and label of the visualization of the DAG. */ -case class RenderedEdge( - colour: String, - label: String = "", - flipped: Boolean = false) - -/** [[InwardNodeImp]] defines the types that describe the inward side of the [[BaseNode]]. - * - * @tparam DI The type of the downward-flowing parameters received on the inner side of the node. - * @tparam UI The type of the upward-flowing parameters generated by the inner side of the node. - * @tparam EI The type of the diplomatically-resolved parameters for an Edge connected to the inner side of the node. - * @tparam BI The type of the [[chisel3.Data]] (usually a [[chisel3.Bundle]]) used when connecting to the inner side of the node, - * corresponding to the real hardware interface that is emitted along the graph edge, - * generally parameterized by the [[EI]] type. - */ -trait InwardNodeImp[DI, UI, EI, BI <: Data] -{ - /** Creates the inward edge parameters by combining the downward-flowing and upward-flowing parameters for edges - * that connect to the inward side of this [[BaseNode]]. - * - * It is left up to a user defining a particular protocol implementation to decide how the parameters flowing through - * the graph in both directions on this Edge are combined into a single representation. - * - * @param pd The downward-flowing parameters into the node along the edge. - * @param pu The upward-flowing parameters going out of the node along the edge. - * @param p A view of [[Parameters]] at the point at which the returned edge is being bound. - * @param sourceInfo [[SourceInfo]] of this edge. - * @return An inward edge of this node. - */ - def edgeI(pd: DI, pu: UI, p: Parameters, sourceInfo: SourceInfo): EI - - /** Create an inward bundle parameterized by the inward edge. - * - * @param ei Inward edge of this node. - * @return An outward Bundle of this node parameterized by the negotiated Edge parameters. - */ - def bundleI(ei: EI): BI - - /** Defines how input parameters can be "mixed" or negotiated together. - * - * The default behavior is to just return `pu`. - * - * @param pu The upward-flowing parameters going out of the node along the edge. - * @param node An inward node to "mix" the upward-flowing parameters into. - * @return Altered version of the upward-flowing parameters. - */ - def mixI(pu: UI, node: InwardNode[DI, UI, BI]): UI = pu - - /** Function to generate and attach a monitor for this node input. - * - * @param bundle Inward bundle of this node to attach the monitor to. - * @param edge Edge of this node used to parameterize the bundle. - */ - def monitor(bundle: BI, edge: EI): Unit = {} - - /** Define how the edge should be rendered (e.g. in GraphML). - * - * @param e Edge to render. - * @return [[RenderedEdge]] description of how the edge should be generated. - */ - def render(e: EI): RenderedEdge -} - -/** [[OutwardNodeImp]] defines the types that describe the outwards side of the [[BaseNode]]. - * - * @tparam DO The type of the downward-flowing parameters generated by the outer side of the node - * @tparam UO Tye type of the upward-flowing parameters received by the outer side of the node - * @tparam EO The type of the diplomatically-resolved parameters for an Edge connected to the outer side of the node. - * @tparam BO The type of the [[chisel3.Data]] (usually a [[chisel3.Bundle]]) used when connecting to the outer side of the node, - * corresponding to the real hardware interface that is emitted along the graph edge, - * generally parameterized by the [[EO]] type. - */ -trait OutwardNodeImp[DO, UO, EO, BO <: Data] -{ - /** Creates the outward edge parameters by combining the downward-flowing and upward-flowing parameters for edges - * that connect to the outward side of this [[BaseNode]]. - * - * It is left up to a user defining a particular protocol implementation to decide how the parameters flowing through - * the graph in both directions on this Edge are combined into a single representation. - * - * @param pd The downward-flowing parameters going out of the node along the edge. - * @param pu The upward-flowing parameters into the node along the edge. - * @param p A view of [[Parameters]] at the point at which the returned edge is being bound. - * @param sourceInfo [[SourceInfo]] of this edge. - * @return An outward edge of this node. - */ - def edgeO(pd: DO, pu: UO, p: Parameters, sourceInfo: SourceInfo): EO - - /** Create an outward Bundle parameterized by the outward edge. - * - * @param eo Outward Edge of this node. - * @return An outward Bundle of this node parameterized by the negotiated Edge parameters. - */ - def bundleO(eo: EO): BO - - /** Defines how outward parameters can be "mixed" or negotiated together. - * - * The default behavior is to just return `pd`. - * - * @param pd The downward-flowing parameters into the node along the edge. - * @param node An outward node to "mix" the downward-flowing parameters into. - * @return Altered version of the downward-flowing parameters. - */ - def mixO(pd: DO, node: OutwardNode[DO, UO, BO]): DO = pd -} - -/** The [[NodeImp]] combines an [[InwardNodeImp]] and an [[OutwardNodeImp]]. - * - * This allows it to define whether it is a protocol-modifying (bridging) sort of node, - * or whether it is an adapter type node that just modifies the parameters within a protocol. - * - * This class has no members and is solely used for holding type information. - * Applications of diplomacy should extend [[NodeImp]] with a case object that sets concrete type arguments. - * - * @tparam D Type of the downward-flowing parameters of the node. - * @tparam U Type of upward-flowing parameters of the node. - * @tparam EO Type of the parameters describing an edge on the outer side of the node. - * @tparam EI Type of the parameters describing an edge on the inner side of the node. - * @tparam B Bundle type generated on edges connecting to this node. - */ -abstract class NodeImp[D, U, EO, EI, B <: Data] extends Object - with InwardNodeImp[D, U, EI, B] - with OutwardNodeImp[D, U, EO, B] - -/** A [[NodeImp]] where the inward and outward edge parameters are of the same type. - * - * If, in a given protocol implementation, the parameters visible to the node on the inward side of an edge are - * the same as the parameters visible to the node on the outward side of an edge, - * [[SimpleNodeImp]] can be used instead of [[NodeImp]]. - * - * @tparam D Type of the downward-flowing parameters of the node. - * @tparam U Type of the upward-flowing parameters of the node. - * @tparam E Edge Parameters describing the connections on either side of the node. - * @tparam B Bundle type generated on edges connecting to this node. - */ -abstract class SimpleNodeImp[D, U, E, B <: Data] - extends NodeImp[D, U, E, E, B] { - /** Creates the edge parameters by combining the downward-flowing and upward-flowing parameters for edges that connect to this node. - * - * It is left up to a user defining a particular protocol implementation to decide how the parameters flowing through the graph in - * both directions are combined into a single representation on an Edge. - * - * @param pd The downward-flowing parameters into the node along the edge. - * @param pu The upward-flowing parameters going out of the node along the edge. - * @param p [[Parameters]]s which can be used during negotiation. - * @param sourceInfo [[SourceInfo]] of this edge. - * @return Negotiated edge parameters. - */ - def edge(pd: D, pu: U, p: Parameters, sourceInfo: SourceInfo): E - - def edgeO(pd: D, pu: U, p: Parameters, sourceInfo: SourceInfo): E = edge(pd, pu, p, sourceInfo) - - def edgeI(pd: D, pu: U, p: Parameters, sourceInfo: SourceInfo): E = edge(pd, pu, p, sourceInfo) - - /** Generate the Bundle from the negotiated Edge parameters. - * - * @param e the negotiated Edge parameters - * @return the corresponding Bundle of this node - */ - def bundle(e: E): B - - def bundleO(e: E): B = bundle(e) - - def bundleI(e: E): B = bundle(e) -} - -/** [[BaseNode]] is the abstract base class of the type hierarchy of diplomacy node classes. - * - * @param valName [[ValName]] of this node, used by naming inference. - */ -abstract class BaseNode(implicit val valName: ValName) { - /** All subclasses of [[BaseNode]]s are expected to be instantiated only within [[LazyModule]]s. - * - * Sometimes one wants to view the entire diplomacy graph in a way - * where you do not care about the specific types of the edges. - * [[BaseNode]]s are type-erased and provide this view. - * - * @return The [[LazyModule]] which contains this Node. - */ - val scope: Option[LazyModule] = LazyModule.scope - - /** @return The index for this node in the containing [[LazyModule]]/[[LazyScope]]'s list of [[BaseNode]]s */ - val index: Int = scope.map(_.nodes.size).getOrElse(0) - - /** @return The [[LazyModule]] which contains this [[BaseNode]] */ - def lazyModule: LazyModule = scope.get - - // Prepend this node to the current [[LazyModule]]'s list of nodes - scope.foreach { lm => lm.nodes = this :: lm.nodes } - - /** @return The serial number for this node in the global list of [[BaseNode]]s. */ - val serial: Int = BaseNode.serial - - BaseNode.serial = BaseNode.serial + 1 - - /** Instantiate this node. - * - * This happens after all nodes connections have been made and we are ready to perform parameter negotiation. - * This also determines which connections need to leave this node's LazyScope and cross hierarchical boundaries. - * That information is captured in [[Dangle]]s which are returned from this function. - * - * @return A sequence of [[Dangle]]s from this node that leave this [[BaseNode]]'s [[LazyScope]]. - */ - protected[diplomacy] def instantiate(): Seq[Dangle] - /** Determine the [[Dangle]]'s for connections without instantiating the node, or any child nodes - * - * @return A sequence of [[Dangle]]s from this node that leave this [[BaseNode]]'s [[LazyScope]]. - */ - protected[diplomacy] def cloneDangles(): Seq[Dangle] - - /** @return name of this node. */ - def name: String = scope.map(_.name).getOrElse("TOP") + "." + valName.name - - /** Determines whether or not this node will be excluded from the graph visualization. - * - * By default, if this node has neither inputs nor outputs it will be excluded. - */ - def omitGraphML: Boolean = outputs.isEmpty && inputs.isEmpty - - /** Debug string of this node, used in [[LazyModule.graphML]]. */ - lazy val nodedebugstring: String = "" - - /** Mark whether this node represents a circuit "identity" that outputs its inputs unchanged. - * - * This information may be used to elide monitors or inline the parent module. - */ - def circuitIdentity: Boolean = false - - /** @return A sequence of [[LazyModule]] up to and including Top. */ - def parents: Seq[LazyModule] = scope.map(lm => lm +: lm.parents).getOrElse(Nil) - - /** @return The context string for debug. */ - def context: String = { - s"""$description $name node: - |parents: ${parents.map(_.name).mkString("/")} - |locator: ${scope.map(_.line).getOrElse("")} - |""".stripMargin - } - - /** Determines the name to be used in elements of auto-punched bundles. - * - * It takes the name of the node as determined from [[valName]], - * converts camel case into snake case, and strips "Node" or "NodeOpt" suffixes. - */ - def wirePrefix: String = { - val camelCase = "([a-z])([A-Z])".r - val decamel = camelCase.replaceAllIn(valName.name, _ match { case camelCase(l, h) => l + "_" + h }) - val name = decamel.toLowerCase.stripSuffix("_opt").stripSuffix("node").stripSuffix("_") - if (name.isEmpty) "" else name + "_" - } - - /** @return [[BaseNode]] description, which should be defined by subclasses and is generally expected to be a constant. */ - def description: String - - /** @return [[BaseNode]] instance description, which can be overridden with more detailed information about each node. */ - def formatNode: String = "" - - /** @return Metadata to visualize inward edges into this node. */ - def inputs: Seq[(BaseNode, RenderedEdge)] - - /** @return Metadata to visualize outward edges from this node. */ - def outputs: Seq[(BaseNode, RenderedEdge)] - - /** @return Whether this node can handle [[BIND_FLEX]] type connections on either side. - * - * For example, a node `b` will have [[flexibleArityDirection]] be `true` if both are legal: - * `a :*=* b :*= c`, which resolves to `a :*= b :*= c` - * or - * `a :=* b :*=* c`, which resolves to `a :=* b :=* c` - * - * If this is `false`, the node can only support `:*=*` if it connects to a node with `flexibleArityDirection = true` - */ - protected[diplomacy] def flexibleArityDirection: Boolean = false - - /** @return The sink cardinality. - * - * How many times is this node used as a sink. - */ - protected[diplomacy] val sinkCard: Int - - /** @return The source cardinality. - * - * How many times is this node used as a source. - */ - protected[diplomacy] val sourceCard: Int - - /** @return The "flex" cardinality. - * - * How many times is this node used in a way that could be either source or sink, depending on final - * directional determination. - */ - protected[diplomacy] val flexes: Seq[BaseNode] - - /** Resolves the flex to be either source or sink. - * - * @return A value >= 0 if it is sink cardinality, a negative value for source cardinality. The magnitude of the value does not matter. - */ - protected[diplomacy] val flexOffset: Int -} - -/** Companion object for [[BaseNode]], which is only used to hold the the global serial number of all [[BaseNode]]s. */ -object BaseNode { - protected[diplomacy] var serial = 0 -} - -/** Trait that enables a string representation of an edge. */ -trait FormatEdge { - def formatEdge: String -} - -/** Trait that enables iterating over a [[BaseNode]]'s edges to produce a formatted string representation. - * - * In practice this is generally GraphML metadata. - */ -trait FormatNode[I <: FormatEdge, O <: FormatEdge] extends BaseNode { - def edges: Edges[I,O] - - /** Format the edges of the [[BaseNode]] for emission (generally in GraphML). */ - override def formatNode = if (circuitIdentity) "" else { - edges.out.map(currEdge => - "On Output Edge:\n\n" + currEdge.formatEdge).mkString + - "\n---------------------------------------------\n\n" + - edges.in.map(currEdge => - "On Input Edge:\n\n" + currEdge.formatEdge).mkString - } -} - -/** A Handle with no explicitly defined binding functionality. - * - * A [[NoHandle]] is at the top of the Handle type hierarchy, but it does not define any binding operators, - * so by itself a [[NoHandle]] cannot be used on either side of a bind operator. - * - * For example, a source node connected directly to a sink node produces a [[NoHandle]], - * because there are no further bindings that could be applied to either side of the pair of nodes. - * - * The other Handle types extend this type and bestow actual binding semantics. - * They can always be used wherever a [[NoHandle]] is expected because a [[NoHandle]] - * doesn't provide any guaranteed behavior. - * - * Handle algebra: - * - * "x---x" [[NoHandle]] - * "x---<" [[InwardNodeHandle]] - * "<---x" [[OutwardNodeHandle]] - * "<---<" (Full) [[NodeHandle]] - * - * "<" can be bound to (arrow points in the direction of binding). - * "x" cannot be bound to. - * - * The left side is outer, the right side is inner. - * - * Two Handles can be bound if their adjacent ends are both "<". - */ -trait NoHandle -case object NoHandleObject extends NoHandle - -/** A Handle that can be used on either side of a bind operator. */ -trait NodeHandle[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data] - extends InwardNodeHandle[DI, UI, EI, BI] with OutwardNodeHandle[DO, UO, EO, BO] { - /** Connects two full nodes handles => full node handle. - * - * <---< := <---< == <---< - * This and that node are both [[BIND_ONCE]]. - * - * @param h A source node also with sink handle. - * @return A [[NodeHandle]] with that node as `inwardNode`, this node as `outwardNode`. - */ - override def := [DX, UX, EX, BX <: Data, EY](h: NodeHandle[DX, UX, EX, BX, DI, UI, EY, BI])(implicit p: Parameters, sourceInfo: SourceInfo): NodeHandle[DX, UX, EX, BX, DO, UO, EO, BO] = { bind(h, BIND_ONCE); NodeHandle(h, this) } - - /** Connects two full nodes handles => full node handle. - * - * <---< :*= <---< == <---< - * [[BIND_STAR]] this node as sink, [[BIND_QUERY]] that node as source. - * - * @param h A source node also with sink handle. - * @return A [[NodeHandle]] with that node as `InwardNode`, this node as `OutwardNode`. - */ - override def :*= [DX, UX, EX, BX <: Data, EY](h: NodeHandle[DX, UX, EX, BX, DI, UI, EY, BI])(implicit p: Parameters, sourceInfo: SourceInfo): NodeHandle[DX, UX, EX, BX, DO, UO, EO, BO] = { bind(h, BIND_STAR); NodeHandle(h, this) } - - /** Connects two full nodes handles => full node handle. - * - * <---< :=* <---< == <---< - * [[BIND_QUERY]] this node as sink, [[BIND_STAR]] that node as source. - * - * @param h A source node also with sink handle. - * @return A [[NodeHandle]] with that node as `InwardNode`, this node as `OutwardNode`. - */ - override def :=* [DX, UX, EX, BX <: Data, EY](h: NodeHandle[DX, UX, EX, BX, DI, UI, EY, BI])(implicit p: Parameters, sourceInfo: SourceInfo): NodeHandle[DX, UX, EX, BX, DO, UO, EO, BO] = { bind(h, BIND_QUERY); NodeHandle(h, this) } - - /** Connects two full nodes handles => full node handle. - * - * <---< :*=* <---< == <---< - * [[BIND_FLEX]] this node as sink, [[BIND_FLEX]] that node as source. - * - * @param h A source node also with sink handle. - * @return A [[NodeHandle]] with that node as `inwardNode`, this node as `outwardNode`. - */ - override def :*=*[DX, UX, EX, BX <: Data, EY](h: NodeHandle[DX, UX, EX, BX, DI, UI, EY, BI])(implicit p: Parameters, sourceInfo: SourceInfo): NodeHandle[DX, UX, EX, BX, DO, UO, EO, BO] = { bind(h, BIND_FLEX); NodeHandle(h, this) } - - /** Connects a full node with an output node => an output handle. - * - * <---< := <---x == <---x - * [[BIND_ONCE]] this node as sink, [[BIND_ONCE]] that node as source. - * - * @param h A source node also without sink handle. - * @return A [[OutwardNodeHandle]] with this node as `outwardNode`. - */ - override def := [EY](h: OutwardNodeHandle[DI, UI, EY, BI])(implicit p: Parameters, sourceInfo: SourceInfo): OutwardNodeHandle[DO, UO, EO, BO] = { bind(h, BIND_ONCE); this } - - /** Connects a full node with an output node => an output handle. - * - * <---< :*= <---x == <---x - * [[BIND_STAR]] this node as sink, [[BIND_QUERY]] that node as source. - * - * @param h A source node also without sink handle. - * @return A [[OutwardNodeHandle]] with this node as `outwardNode`. - */ - override def :*= [EY](h: OutwardNodeHandle[DI, UI, EY, BI])(implicit p: Parameters, sourceInfo: SourceInfo): OutwardNodeHandle[DO, UO, EO, BO] = { bind(h, BIND_STAR); this } - - /** Connects a full node with an output => an output. - * - * <---< :=* <---x == <---x - * [[BIND_QUERY]] this node as sink, [[BIND_STAR]] that node as source. - * - * @param h A source node also without sink handle. - * @return A [[OutwardNodeHandle]] with this node as `outwardNode`. - */ - override def :=* [EY](h: OutwardNodeHandle[DI, UI, EY, BI])(implicit p: Parameters, sourceInfo: SourceInfo): OutwardNodeHandle[DO, UO, EO, BO] = { bind(h, BIND_QUERY); this } - - /** Connects a full node with an output => an output. - * - * <---< :*=* <---x == <---x - * [[BIND_FLEX]] this node as sink, [[BIND_FLEX]] that node as source. - * - * @param h A source node also without sink handle. - * @return A [[OutwardNodeHandle]] with this node as `outwardNode`. - */ - override def :*=*[EY](h: OutwardNodeHandle[DI, UI, EY, BI])(implicit p: Parameters, sourceInfo: SourceInfo): OutwardNodeHandle[DO, UO, EO, BO] = { bind(h, BIND_FLEX); this } -} - -object NodeHandle { - /** generate a [[NodeHandle]] by combining an [[InwardNodeHandle]] and an [[OutwardNodeHandle]]. - * - * @param i Inward node handle. - * @param o Outward node handle. - * @return [[NodeHandlePair]] with `inwardNode` of `i`, `outwardNode` of `o`. - */ - def apply[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data](i: InwardNodeHandle[DI, UI, EI, BI], o: OutwardNodeHandle[DO, UO, EO, BO]) = new NodeHandlePair(i, o) -} - -/** A data structure that preserves information about the innermost and outermost Nodes in a [[NodeHandle]]. */ -class NodeHandlePair[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data] - (inwardHandle: InwardNodeHandle[DI, UI, EI, BI], outwardHandle: OutwardNodeHandle[DO, UO, EO, BO]) - extends NodeHandle[DI, UI, EI, BI, DO, UO, EO, BO] { - /** @return [[InwardNode]] of [[inwardHandle]]. */ - val inward: InwardNode[DI, UI, BI] = inwardHandle.inward - - /** @return [[OutwardNode]] of [[outwardHandle]]. */ - val outward: OutwardNode[DO, UO, BO] = outwardHandle.outward - - /** @return The innermost [[InwardNodeImp]] of this [[NodeHandlePair]]. */ - def inner: InwardNodeImp[DI, UI, EI, BI] = inwardHandle.inner - - /** @return The outermost [[OutwardNodeImp]] of [[NodeHandlePair]]. */ - def outer: OutwardNodeImp[DO, UO, EO, BO] = outwardHandle.outer -} - -/** A handle for an [[InwardNode]], which may appear on the left side of a bind operator. */ -trait InwardNodeHandle[DI, UI, EI, BI <: Data] extends NoHandle -{ - /** @return [[InwardNode]] of `inwardHandle`. */ - def inward: InwardNode[DI, UI, BI] - - /** @return [[InwardNodeImp]] of `inwardHandle`. */ - def inner: InwardNodeImp[DI, UI, EI, BI] - - /** Bind this node to an [[OutwardNodeHandle]]. */ - protected def bind[EY](h: OutwardNodeHandle[DI, UI, EY, BI], binding: NodeBinding)(implicit p: Parameters, sourceInfo: SourceInfo): Unit = inward.bind(h.outward, binding) - - /** Connect an input node with a full node => inward node handle. - * - * x---< := <---< == x---< - * [[BIND_ONCE]] this node as sink, [[BIND_ONCE]] that node as source. - * - * @param h A source node also with sink handle. - * @return A [[NodeHandle]] with that node as `inwardNode`, this node as `outwardNode`. - */ - def := [DX, UX, EX, BX <: Data, EY](h: NodeHandle[DX, UX, EX, BX, DI, UI, EY, BI])(implicit p: Parameters, sourceInfo: SourceInfo): InwardNodeHandle[DX, UX, EX, BX] = { bind(h, BIND_ONCE); h } - - /** Connect an input node with a full node => an input node. - * - * x---< :*= <---< == x---< - * [[BIND_STAR]] this node as sink, [[BIND_QUERY]] that node as source. - * - * @param h A Source node also with sink handle. - * @return A [[NodeHandle]] with that node as `inwardNode`, this node as `outwardNode`. - */ - def :*= [DX, UX, EX, BX <: Data, EY](h: NodeHandle[DX, UX, EX, BX, DI, UI, EY, BI])(implicit p: Parameters, sourceInfo: SourceInfo): InwardNodeHandle[DX, UX, EX, BX] = { bind(h, BIND_STAR); h } - - /** Connect an input node with a full node => an inward node handle. - * - * x---< :=* <---< == x---< - * [[BIND_QUERY]] this node as sink, [[BIND_STAR]] that node as source. - * - * @param h A source node also with sink handle. - * @return A [[NodeHandle]] with that node as `inwardNode`, this node as `outwardNode`. - */ - def :=* [DX, UX, EX, BX <: Data, EY](h: NodeHandle[DX, UX, EX, BX, DI, UI, EY, BI])(implicit p: Parameters, sourceInfo: SourceInfo): InwardNodeHandle[DX, UX, EX, BX] = { bind(h, BIND_QUERY); h } - - /** Connect an input node with a full node => an input node. - * - * x---< :*=* <---< == x---< - * [[BIND_FLEX]] this node as sink, [[BIND_FLEX]] that node as source. - * - * @param h A source node also with sink handle. - * @return A [[NodeHandle]] with that node as `inwardNode`, this node as `outwardNode`. - */ - def :*=*[DX, UX, EX, BX <: Data, EY](h: NodeHandle[DX, UX, EX, BX, DI, UI, EY, BI])(implicit p: Parameters, sourceInfo: SourceInfo): InwardNodeHandle[DX, UX, EX, BX] = { bind(h, BIND_FLEX); h } - - /** Connect an input node with output node => no node. - * - * x---< := <---x == x---x - * [[BIND_ONCE]] this node as sink, [[BIND_ONCE]] that node as source. - * - * @param h A source node also without sink handle. - * @return A [[NoHandle]] since neither side can bind to a node. - */ - def := [EY](h: OutwardNodeHandle[DI, UI, EY, BI])(implicit p: Parameters, sourceInfo: SourceInfo): NoHandle = { bind(h, BIND_ONCE); NoHandleObject } - - /** Connect an input node with output node => no node. - * - * x---< :*= <---x == x---x - * [[BIND_STAR]] this node as sink, [[BIND_QUERY]] that node as source. - * - * @param h A source node also without sink handle. - * @return A [[NoHandle]] since neither side can bind to a node. - */ - def :*= [EY](h: OutwardNodeHandle[DI, UI, EY, BI])(implicit p: Parameters, sourceInfo: SourceInfo): NoHandle = { bind(h, BIND_STAR); NoHandleObject } - - /** Connect an input node with output node => no node. - * - * x---< :=* <---x == x---x - * [[BIND_QUERY]] this node as sink, [[BIND_STAR]] that node as source. - * - * @param h A source node also without sink handle. - * @return A [[NoHandle]] since neither side can bind to another node. - */ - def :=* [EY](h: OutwardNodeHandle[DI, UI, EY, BI])(implicit p: Parameters, sourceInfo: SourceInfo): NoHandle = { bind(h, BIND_QUERY); NoHandleObject } - - /** Connect an input node with output node => no node. - * - * x---< :*=* <---x == x---x - * [[BIND_FLEX]] this node as sink, [[BIND_FLEX]] that node as source. - * - * @param h A source node also without sink handle. - * @return A [[NoHandle]] since neither side can bind to another node. - */ - def :*=*[EY](h: OutwardNodeHandle[DI, UI, EY, BI])(implicit p: Parameters, sourceInfo: SourceInfo): NoHandle = { bind(h, BIND_FLEX); NoHandleObject } -} - -/** Enumeration of types of binding operations. */ -sealed trait NodeBinding - -/** Only connects a single edge. */ -case object BIND_ONCE extends NodeBinding { - override def toString: String = "once" -} - -/** Connects N (N >= 0) edges. - * - * The other side of the edge determines cardinality. - */ -case object BIND_QUERY extends NodeBinding { - override def toString: String = "query" -} - -/** Connect N (N >= 0) edges. - * - * Our side of the edge determines cardinality. - */ -case object BIND_STAR extends NodeBinding { - override def toString: String = "star" -} - -/** Connect N (N >= 0) connections. - * - * The number of edges N will be determined by either the right or left side, - * once the direction ([[BIND_STAR]] or [[BIND_QUERY]]) is determined by the other connections as well. - */ -case object BIND_FLEX extends NodeBinding { - override def toString: String = "flex" -} - -/** A Node that defines inward behavior, meaning that it can have edges coming into it and be used on the left side of binding expressions. */ -trait InwardNode[DI, UI, BI <: Data] extends BaseNode { - /** accumulates input connections. */ - private val accPI = ListBuffer[(Int, OutwardNode[DI, UI, BI], NodeBinding, Parameters, SourceInfo)]() - - /** Initially `false`, set to `true` once [[iBindings]] has been evaluated. */ - private var iRealized = false - - /** @return debug information of [[iBindings]]. */ - def iBindingInfo: String = s"""${iBindings.size} inward nodes bound: [${iBindings.map(n => s"${n._3}-${n._2.name}").mkString(",")}]""" - - - /** The accumulated number of input connections. */ - protected[diplomacy] def iPushed: Int = accPI.size - - /** Accumulate an input connection. - * - * Can only be called before [[iBindings]] is accessed. - * - * @param index index of this [[InwardNode]] in that [[OutwardNode]]. - * @param node the [[OutwardNode]] to bind to this [[InwardNode]]. - * @param binding [[NodeBinding]] type. - */ - protected[diplomacy] def iPush(index: Int, node: OutwardNode[DI, UI, BI], binding: NodeBinding)(implicit p: Parameters, sourceInfo: SourceInfo): Unit = { - val info = sourceLine(sourceInfo, " at ", "") - require (!iRealized, - s"""Diplomacy has detected a problem in your code: - |The following node was incorrectly connected as a sink to ${node.name} after its .module was evaluated at $info. - |$context - |$iBindingInfo - |""".stripMargin) - accPI += ((index, node, binding, p, sourceInfo)) - } - - /** Ends the binding accumulation stage and returns all the input bindings to this node. - * - * Evaluating this lazy val will mark the inwards bindings as frozen, - * preventing subsequent bindings from being created via [[iPush]]. - * - * The bindings are each a tuple of: - * - numeric index of this binding in the other end of [[OutwardNode]]. - * - [[OutwardNode]] on the other end of this binding. - * - [[NodeBinding]] describing the type of binding. - * - A view of [[Parameters]] where the binding occurred. - * - [[SourceInfo]] for source-level error reporting. - */ - protected[diplomacy] lazy val iBindings: immutable.Seq[(Int, OutwardNode[DI, UI, BI], NodeBinding, Parameters, SourceInfo)] = { iRealized = true; accPI.result() } - - /** resolved [[BIND_STAR]] binding of inward nodes: how many connections the star represents. */ - protected[diplomacy] val iStar: Int - - /** A mapping to convert Node binding index to port range. - * - * @return a sequence of tuple of mapping, the item in each a tuple of: - * - index: the index of connected [[OutwardNode]] - * - element: port range of connected [[OutwardNode]] - */ - protected[diplomacy] val iPortMapping: Seq[(Int, Int)] - - /** "Forward" an input connection through this node so that the node can be removed from the graph. - * - * @return None if no forwarding is needing. - */ - protected[diplomacy] def iForward(x: Int): Option[(Int, InwardNode[DI, UI, BI])] = None - - /** Downward-flowing inward parameters. - * - * Generated from the nodes connected to the inward side of this node and sent downstream to this node. - */ - protected[diplomacy] val diParams: Seq[DI] - - /** Upward-flowing inward parameters. - * - * Generated by this node and sent upstream to the nodes connected to the inward side of this node. - */ - protected[diplomacy] val uiParams: Seq[UI] - - /** Create a binding from this node to an [[OutwardNode]]. - * - * @param h The [[OutwardNode]] to bind to. - * @param binding [[NodeBinding]] the type of binding. - */ - protected[diplomacy] def bind(h: OutwardNode[DI, UI, BI], binding: NodeBinding)(implicit p: Parameters, sourceInfo: SourceInfo): Unit -} - -/** A Handle for OutwardNodes, which may appear on the right side of a bind operator. */ -trait OutwardNodeHandle[DO, UO, EO, BO <: Data] extends NoHandle { - /** @return [[OutwardNode]] of `outwardHandle`. */ - def outward: OutwardNode[DO, UO, BO] - - /** @return [[OutwardNodeImp]] of `inwardHandle`. */ - def outer: OutwardNodeImp[DO, UO, EO, BO] -} - -/** A Node that defines outward behavior, meaning that it can have edges coming out of it. */ -trait OutwardNode[DO, UO, BO <: Data] extends BaseNode { - /** Accumulates output connections. */ - private val accPO = ListBuffer[(Int, InwardNode [DO, UO, BO], NodeBinding, Parameters, SourceInfo)]() - - /** Initially set to `true`, this is set to false once [[oBindings]] is referenced. */ - private var oRealized = false - - /** @return debug information of [[oBindings]]. */ - def oBindingInfo: String = s"""${oBindings.size} outward nodes bound: [${oBindings.map(n => s"${n._3}-${n._2.name}").mkString(",")}]""" - - /** The accumulated number of output connections of this node. */ - protected[diplomacy] def oPushed: Int = accPO.size - - /** Accumulate an output connection. - * - * Can only be called before [[oBindings]] is accessed. - * - * @param index Index of this [[OutwardNode]] in that [[InwardNode]]. - * @param node [[InwardNode]] to bind to. - * @param binding Binding type. - */ - protected[diplomacy] def oPush(index: Int, node: InwardNode [DO, UO, BO], binding: NodeBinding)(implicit p: Parameters, sourceInfo: SourceInfo): Unit = { - val info = sourceLine(sourceInfo, " at ", "") - require (!oRealized, - s"""Diplomacy has detected a problem in your code: - |The following node was incorrectly connected as a source to ${node.name} after its .module was evaluated at $info. - |$context - |$oBindingInfo - |""".stripMargin) - accPO += ((index, node, binding, p, sourceInfo)) - } - - /** Ends the binding accumulation stage and returns all the output bindings to this node. - * - * Evaluating this lazy val will mark the outward bindings as frozen, - * preventing subsequent bindings from being created via [[oPush]]. - * - * The bindings are each a tuple of: - * - numeric index of this binding in the other end of [[InwardNode]]. - * - [[InwardNode]] on the other end of this binding - * - [[NodeBinding]] describing the type of binding - * - A view of [[Parameters]] where the binding occurred. - * - [[SourceInfo]] for source-level error reporting - */ - protected[diplomacy] lazy val oBindings: Seq[(Int, InwardNode[DO, UO, BO], NodeBinding, Parameters, SourceInfo)] = { oRealized = true; accPO.result() } - - /** resolved [[BIND_STAR]] binding of outward nodes: how many connections the star represents. */ - protected[diplomacy] val oStar: Int - - /** A mapping to convert Node binding index to port range. - * - * @return a sequence of tuple of mapping, the item in each a tuple of: - * - index: the index of connected [[InwardNode]] - * - element: port range of connected [[InwardNode]] - */ - protected[diplomacy] val oPortMapping: Seq[(Int, Int)] - - /** "Forward" an output connection through this node so that the node can be removed from the graph. - * - * @return None if no forwarding is needed. - */ - protected[diplomacy] def oForward(x: Int): Option[(Int, OutwardNode[DO, UO, BO])] = None - - /** Upward-flowing outward parameters. - * - * Generated from the nodes connected to the outward side of this node and sent upstream to this node. - */ - protected[diplomacy] val uoParams: Seq[UO] - - /** Downward-flowing outward parameters. - * - * Generated by this node and sent downstream to the nodes connected to the outward side of this node. - */ - protected[diplomacy] val doParams: Seq[DO] -} - -abstract class CycleException(kind: String, loop: Seq[String]) extends Exception(s"Diplomatic $kind cycle detected involving $loop") -case class StarCycleException(loop: Seq[String] = Nil) extends CycleException("star", loop) -case class DownwardCycleException(loop: Seq[String] = Nil) extends CycleException("downward", loop) -case class UpwardCycleException(loop: Seq[String] = Nil) extends CycleException("upward", loop) - -/** [[Edges]] is a collection of parameters describing the functionality and connection for an interface, - * which is often derived from the interconnection protocol and can inform the parameterization - * of the hardware bundles that actually implement the protocol. - */ -case class Edges[EI, EO](in: Seq[EI], out: Seq[EO]) - -/** The sealed node class in the package, all node are derived from it. - * - * @param inner Sink interface implementation. - * @param outer Source interface implementation. - * @param valName val name of this node. - * @tparam DI Downward-flowing parameters received on the inner side of the node. - * It is usually a brunch of parameters describing the protocol parameters from a source. - * For an [[InwardNode]], it is determined by the connected [[OutwardNode]]. - * Since it can be connected to multiple sources, this parameter is always a Seq of source port parameters. - * @tparam UI Upward-flowing parameters generated by the inner side of the node. - * It is usually a brunch of parameters describing the protocol parameters of a sink. - * For an [[InwardNode]], it is determined itself. - * @tparam EI Edge Parameters describing a connection on the inner side of the node. - * It is usually a brunch of transfers specified for a sink according to protocol. - * @tparam BI Bundle type used when connecting to the inner side of the node. - * It is a hardware interface of this sink interface. - * It should extends from [[chisel3.Data]], which represents the real hardware. - * @tparam DO Downward-flowing parameters generated on the outer side of the node. - * It is usually a brunch of parameters describing the protocol parameters of a source. - * For an [[OutwardNode]], it is determined itself. - * @tparam UO Upward-flowing parameters received by the outer side of the node. - * It is usually a brunch of parameters describing the protocol parameters from a sink. - * For an [[OutwardNode]], it is determined by the connected [[InwardNode]]. - * Since it can be connected to multiple sinks, this parameter is always a Seq of sink port parameters. - * @tparam EO Edge Parameters describing a connection on the outer side of the node. - * It is usually a brunch of transfers specified for a source according to protocol. - * @tparam BO Bundle type used when connecting to the outer side of the node. - * It is a hardware interface of this source interface. - * It should extends from [[chisel3.Data]], which represents the real hardware. - * - * @note Call Graph of [[MixedNode]] - * - line `─`: source is process by a function and generate pass to others - * - Arrow `→`: target of arrow is generated by source - * - * {{{ - * (from the other node) - * ┌─────────────────────────────────────────────────────────[[InwardNode.uiParams]]─────────────┐ - * ↓ │ - * (binding node when elaboration) [[OutwardNode.uoParams]]────────────────────────[[MixedNode.mapParamsU]]→──────────┐ │ - * [[InwardNode.accPI]] │ │ │ - * │ │ (based on protocol) │ - * │ │ [[MixedNode.inner.edgeI]] │ - * │ │ ↓ │ - * ↓ │ │ │ - * (immobilize after elaboration) (inward port from [[OutwardNode]]) │ ↓ │ - * [[InwardNode.iBindings]]──┐ [[MixedNode.iDirectPorts]]────────────────────→[[MixedNode.iPorts]] [[InwardNode.uiParams]] │ - * │ │ ↑ │ │ │ - * │ │ │ [[OutwardNode.doParams]] │ │ - * │ │ │ (from the other node) │ │ - * │ │ │ │ │ │ - * │ │ │ │ │ │ - * │ │ │ └────────┬──────────────┤ │ - * │ │ │ │ │ │ - * │ │ │ │ (based on protocol) │ - * │ │ │ │ [[MixedNode.inner.edgeI]] │ - * │ │ │ │ │ │ - * │ │ (from the other node) │ ↓ │ - * │ └───[[OutwardNode.oPortMapping]] [[OutwardNode.oStar]] │ [[MixedNode.edgesIn]]───┐ │ - * │ ↑ ↑ │ │ ↓ │ - * │ │ │ │ │ [[MixedNode.in]] │ - * │ │ │ │ ↓ ↑ │ - * │ (solve star connection) │ │ │ [[MixedNode.bundleIn]]──┘ │ - * ├───[[MixedNode.resolveStar]]→─┼─────────────────────────────┤ └────────────────────────────────────┐ │ - * │ │ │ [[MixedNode.bundleOut]]─┐ │ │ - * │ │ │ ↑ ↓ │ │ - * │ │ │ │ [[MixedNode.out]] │ │ - * │ ↓ ↓ │ ↑ │ │ - * │ ┌─────[[InwardNode.iPortMapping]] [[InwardNode.iStar]] [[MixedNode.edgesOut]]──┘ │ │ - * │ │ (from the other node) ↑ │ │ - * │ │ │ │ │ │ - * │ │ │ [[MixedNode.outer.edgeO]] │ │ - * │ │ │ (based on protocol) │ │ - * │ │ │ │ │ │ - * │ │ │ ┌────────────────────────────────────────┤ │ │ - * │ │ │ │ │ │ │ - * │ │ │ │ │ │ │ - * │ │ │ │ │ │ │ - * (immobilize after elaboration)│ ↓ │ │ │ │ - * [[OutwardNode.oBindings]]─┘ [[MixedNode.oDirectPorts]]───→[[MixedNode.oPorts]] [[OutwardNode.doParams]] │ │ - * ↑ (inward port from [[OutwardNode]]) │ │ │ │ - * │ ┌─────────────────────────────────────────┤ │ │ │ - * │ │ │ │ │ │ - * │ │ │ │ │ │ - * [[OutwardNode.accPO]] │ ↓ │ │ │ - * (binding node when elaboration) │ [[InwardNode.diParams]]─────→[[MixedNode.mapParamsD]]────────────────────────────┘ │ │ - * │ ↑ │ │ - * │ └──────────────────────────────────────────────────────────────────────────────────────────┘ │ - * └──────────────────────────────────────────────────────────────────────────────────────────────────────────┘ - * }}} - */ -sealed abstract class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( - val inner: InwardNodeImp [DI, UI, EI, BI], - val outer: OutwardNodeImp[DO, UO, EO, BO])( - implicit valName: ValName) - extends BaseNode with NodeHandle[DI, UI, EI, BI, DO, UO, EO, BO] with InwardNode[DI, UI, BI] with OutwardNode[DO, UO, BO] { - // Generate a [[NodeHandle]] with inward and outward node are both this node. - val inward = this - val outward = this - - /** Debug info of nodes binding. */ - def bindingInfo: String = - s"""$iBindingInfo - |$oBindingInfo - |""".stripMargin - - /** Debug info of ports connecting. */ - def connectedPortsInfo: String = - s"""${oPorts.size} outward ports connected: [${oPorts.map(_._2.name).mkString(",")}] - |${iPorts.size} inward ports connected: [${iPorts.map(_._2.name).mkString(",")}] - |""".stripMargin - - /** Debug info of parameters propagations. */ - def parametersInfo: String = - s"""${doParams.size} downstream outward parameters: [${doParams.mkString(",")}] - |${uoParams.size} upstream outward parameters: [${uoParams.mkString(",")}] - |${diParams.size} downstream inward parameters: [${diParams.mkString(",")}] - |${uiParams.size} upstream inward parameters: [${uiParams.mkString(",")}] - |""".stripMargin - - /** For a given node, converts [[OutwardNode.accPO]] and [[InwardNode.accPI]] to [[MixedNode.oPortMapping]] and [[MixedNode.iPortMapping]]. - * - * Given counts of known inward and outward binding and inward and outward star bindings, return the resolved inward stars and outward stars. - * - * This method will also validate the arguments and throw a runtime error if the values are unsuitable for this type of node. - * - * @param iKnown Number of known-size ([[BIND_ONCE]]) input bindings. - * @param oKnown Number of known-size ([[BIND_ONCE]]) output bindings. - * @param iStar Number of unknown size ([[BIND_STAR]]) input bindings. - * @param oStar Number of unknown size ([[BIND_STAR]]) output bindings. - * @return A Tuple of the resolved number of input and output connections. - */ - protected[diplomacy] def resolveStar(iKnown: Int, oKnown: Int, iStar: Int, oStar: Int): (Int, Int) - - /** Function to generate downward-flowing outward params from the downward-flowing input params and the current output ports. - * - * @param n The size of the output sequence to generate. - * @param p Sequence of downward-flowing input parameters of this node. - * @return A `n`-sized sequence of downward-flowing output edge parameters. - */ - protected[diplomacy] def mapParamsD(n: Int, p: Seq[DI]): Seq[DO] - - /** Function to generate upward-flowing input parameters from the upward-flowing output parameters [[uiParams]]. - * - * @param n Size of the output sequence. - * @param p Upward-flowing output edge parameters. - * @return A n-sized sequence of upward-flowing input edge parameters. - */ - protected[diplomacy] def mapParamsU(n: Int, p: Seq[UO]): Seq[UI] - - /** @return The sink cardinality of the node, the number of outputs bound with [[BIND_QUERY]] summed with inputs bound with [[BIND_STAR]]. */ - protected[diplomacy] lazy val sinkCard: Int = oBindings.count(_._3 == BIND_QUERY) + iBindings.count(_._3 == BIND_STAR) - - /** @return The source cardinality of this node, the number of inputs bound with [[BIND_QUERY]] summed with the number of output bindings bound with [[BIND_STAR]]. */ - protected[diplomacy] lazy val sourceCard: Int = iBindings.count(_._3 == BIND_QUERY) + oBindings.count(_._3 == BIND_STAR) - - /** @return list of nodes involved in flex bindings with this node. */ - protected[diplomacy] lazy val flexes: Seq[BaseNode] = oBindings.filter(_._3 == BIND_FLEX).map(_._2) ++ iBindings.filter(_._3 == BIND_FLEX).map(_._2) - - /** Resolves the flex to be either source or sink and returns the offset where the [[BIND_STAR]] operators begin greedily taking up the remaining connections. - * - * @return A value >= 0 if it is sink cardinality, a negative value for source cardinality. The magnitude of the return value is not relevant. - */ - protected[diplomacy] lazy val flexOffset: Int = { - /** Recursively performs a depth-first search of the [[flexes]], - * [[BaseNode]]s connected to this node with flex operators. - * The algorithm bottoms out when we either get to a node we have already visited - * or when we get to a connection that is not a flex and can set the direction for us. - * Otherwise, recurse by visiting the `flexes` of each node in the current set - * and decide whether they should be added to the set or not. - * - * @return the mapping of [[BaseNode]] indexed by their serial numbers. - */ - def DFS(v: BaseNode, visited: Map[Int, BaseNode]): Map[Int, BaseNode] = { - if (visited.contains(v.serial) || !v.flexibleArityDirection) { - visited - } else { - v.flexes.foldLeft(visited + (v.serial -> v))((sum, n) => DFS(n, sum)) - } - } - - /** Determine which [[BaseNode]] are involved in resolving the flex connections to/from this node. - * - * @example - * {{{ - * a :*=* b :*=* c - * d :*=* b - * e :*=* f - * }}} - * - * `flexSet` for `a`, `b`, `c`, or `d` will be `Set(a, b, c, d)` - * `flexSet` for `e` or `f` will be `Set(e,f)` - */ - val flexSet = DFS(this, Map()).values - - /** The total number of :*= operators where we're on the left. */ - val allSink = flexSet.map(_.sinkCard).sum - - /** The total number of :=* operators used when we're on the right. */ - val allSource = flexSet.map(_.sourceCard).sum - - require (allSink == 0 || allSource == 0, - s"The nodes ${flexSet.map(_.name)} which are inter-connected by :*=* have ${allSink} :*= operators and ${allSource} :=* operators connected to them, making it impossible to determine cardinality inference direction.") - allSink - allSource - } - - /** @return A value >= 0 if it is sink cardinality, a negative value for source cardinality. */ - protected[diplomacy] def edgeArityDirection(n: BaseNode): Int = { - if ( flexibleArityDirection) flexOffset else - if (n.flexibleArityDirection) n.flexOffset else - 0 - } - - /** For a node which is connected between two nodes, select the one that will influence the direction of the flex resolution. */ - protected[diplomacy] def edgeAritySelect(n: BaseNode, l: => Int, r: => Int): Int = { - val dir = edgeArityDirection(n) - if (dir < 0) l else if (dir > 0) r else 1 - } - - /** Ensure that the same node is not visited twice in resolving `:*=`, etc operators. */ - private var starCycleGuard = false - - /** Resolve all the star operators into concrete indicies. - * As connections are being made, some may be "star" connections which need to be resolved. - * In some way to determine how many actual edges they correspond to. - * We also need to build up the ranges of edges which correspond to each binding operator, so that - * We can apply the correct edge parameters and later build up correct bundle connections. - * - * [[oPortMapping]]: `Seq[(Int, Int)]` where each item is the range of edges corresponding to that oPort (binding operator). - * [[iPortMapping]]: `Seq[(Int, Int)]` where each item is the range of edges corresponding to that iPort (binding operator). - * [[oStar]]: `Int` the value to return for this node `N` for any `N :*= foo` or `N :*=* foo :*= bar` - * [[iStar]]: `Int` the value to return for this node `N` for any `foo :=* N` or `bar :=* foo :*=* N` - */ - protected[diplomacy] lazy val (oPortMapping: Seq[(Int, Int)], iPortMapping: Seq[(Int, Int)], oStar: Int, iStar: Int) = { - try { - if (starCycleGuard) throw StarCycleException() - starCycleGuard = true - // For a given node N... - // Number of foo :=* N - // + Number of bar :=* foo :*=* N - val oStars = oBindings.count { case (_,n,b,_,_) => b == BIND_STAR || (b == BIND_FLEX && edgeArityDirection(n) < 0) } - // Number of N :*= foo - // + Number of N :*=* foo :*= bar - val iStars = iBindings.count { case (_,n,b,_,_) => b == BIND_STAR || (b == BIND_FLEX && edgeArityDirection(n) > 0) } - // 1 for foo := N - // + bar.iStar for bar :*= foo :*=* N - // + foo.iStar for foo :*= N - // + 0 for foo :=* N - val oKnown = oBindings.map { case (_, n, b, _, _) => b match { - case BIND_ONCE => 1 - case BIND_FLEX => edgeAritySelect(n, 0, n.iStar) - case BIND_QUERY => n.iStar - case BIND_STAR => 0 }}.sum - // 1 for N := foo - // + bar.oStar for N :*=* foo :=* bar - // + foo.oStar for N :=* foo - // + 0 for N :*= foo - val iKnown = iBindings.map { case (_, n, b, _, _) => b match { - case BIND_ONCE => 1 - case BIND_FLEX => edgeAritySelect(n, n.oStar, 0) - case BIND_QUERY => n.oStar - case BIND_STAR => 0 }}.sum - // Resolve star depends on the node subclass to implement the algorithm for this. - val (iStar, oStar) = resolveStar(iKnown, oKnown, iStars, oStars) - // Cumulative list of resolved outward binding range starting points - val oSum = oBindings.map { case (_, n, b, _, _) => b match { - case BIND_ONCE => 1 - case BIND_FLEX => edgeAritySelect(n, oStar, n.iStar) - case BIND_QUERY => n.iStar - case BIND_STAR => oStar }}.scanLeft(0)(_+_) - // Cumulative list of resolved inward binding range starting points - val iSum = iBindings.map { case (_, n, b, _, _) => b match { - case BIND_ONCE => 1 - case BIND_FLEX => edgeAritySelect(n, n.oStar, iStar) - case BIND_QUERY => n.oStar - case BIND_STAR => iStar }}.scanLeft(0)(_+_) - // Create ranges for each binding based on the running sums and return - // those along with resolved values for the star operations. - (oSum.init zip oSum.tail, iSum.init zip iSum.tail, oStar, iStar) - } catch { - case c: StarCycleException => throw c.copy(loop = context +: c.loop) - } - } - - /** Sequence of inward ports. - * - * This should be called after all star bindings are resolved. - * - * Each element is: - * `j` Port index of this binding in the Node's [[oPortMapping]] on the other side of the binding. - * `n` Instance of inward node. - * `p` View of [[Parameters]] where this connection was made. - * `s` Source info where this connection was made in the source code. - */ - protected[diplomacy] lazy val oDirectPorts: Seq[(Int, InwardNode[DO, UO, BO], Parameters, SourceInfo)] = oBindings.flatMap { case (i, n, _, p, s) => - // for each binding operator in this node, look at what it connects to - val (start, end) = n.iPortMapping(i) - (start until end) map { j => (j, n, p, s) } - } - - /** Sequence of outward ports. - * - * This should be called after all star bindings are resolved. - * - * `j` Port index of this binding in the Node's [[oPortMapping]] on the other side of the binding. - * `n` Instance of outward node. - * `p` View of [[Parameters]] where this connection was made. - * `s` [[SourceInfo]] where this connection was made in the source code. - */ - protected[diplomacy] lazy val iDirectPorts: Seq[(Int, OutwardNode[DI, UI, BI], Parameters, SourceInfo)] = iBindings.flatMap { case (i, n, _, p, s) => - // query this port index range of this node in the other side of node. - val (start, end) = n.oPortMapping(i) - (start until end) map { j => (j, n, p, s) } - } - - // Ephemeral nodes ( which have non-None iForward/oForward) have in_degree = out_degree - // Thus, there must exist an Eulerian path and the below algorithms terminate - @scala.annotation.tailrec - private def oTrace(tuple: (Int, InwardNode[DO, UO, BO], Parameters, SourceInfo)): (Int, InwardNode[DO, UO, BO], Parameters, SourceInfo) = - tuple match { case (i, n, p, s) => n.iForward(i) match { - case None => (i, n, p, s) - case Some ((j, m)) => oTrace((j, m, p, s)) - } } - - @scala.annotation.tailrec - private def iTrace(tuple: (Int, OutwardNode[DI, UI, BI], Parameters, SourceInfo)): (Int, OutwardNode[DI, UI, BI], Parameters, SourceInfo) = - tuple match { case (i, n, p, s) => n.oForward(i) match { - case None => (i, n, p, s) - case Some ((j, m)) => iTrace((j, m, p, s)) - } } - - /** Final output ports after all stars and port forwarding (e.g. [[EphemeralNode]]s) have been resolved. - * - * Each Port is a tuple of: - * - Numeric index of this binding in the [[InwardNode]] on the other end. - * - [[InwardNode]] on the other end of this binding. - * - A view of [[Parameters]] where the binding occurred. - * - [[SourceInfo]] for source-level error reporting. - */ - lazy val oPorts: Seq[(Int, InwardNode[DO, UO, BO], Parameters, SourceInfo)] = oDirectPorts.map(oTrace) - - /** Final input ports after all stars and port forwarding (e.g. [[EphemeralNode]]s) have been resolved. - * - * Each Port is a tuple of: - * - numeric index of this binding in [[OutwardNode]] on the other end. - * - [[OutwardNode]] on the other end of this binding. - * - a view of [[Parameters]] where the binding occurred. - * - [[SourceInfo]] for source-level error reporting. - */ - lazy val iPorts: Seq[(Int, OutwardNode[DI, UI, BI], Parameters, SourceInfo)] = iDirectPorts.map(iTrace) - - private var oParamsCycleGuard = false - protected[diplomacy] lazy val diParams: Seq[DI] = iPorts.map { case (i, n, _, _) => n.doParams(i) } - protected[diplomacy] lazy val doParams: Seq[DO] = { - try { - if (oParamsCycleGuard) throw DownwardCycleException() - oParamsCycleGuard = true - val o = mapParamsD(oPorts.size, diParams) - require (o.size == oPorts.size, - s"""Diplomacy has detected a problem with your graph: - |At the following node, the number of outward ports should equal the number of produced outward parameters. - |$context - |$connectedPortsInfo - |Downstreamed inward parameters: [${diParams.mkString(",")}] - |Produced outward parameters: [${o.mkString(",")}] - |""".stripMargin) - o.map(outer.mixO(_, this)) - } catch { - case c: DownwardCycleException => throw c.copy(loop = context +: c.loop) - } - } - - private var iParamsCycleGuard = false - protected[diplomacy] lazy val uoParams: Seq[UO] = oPorts.map { case (o, n, _, _) => n.uiParams(o) } - protected[diplomacy] lazy val uiParams: Seq[UI] = { - try { - if (iParamsCycleGuard) throw UpwardCycleException() - iParamsCycleGuard = true - val i = mapParamsU(iPorts.size, uoParams) - require (i.size == iPorts.size, - s"""Diplomacy has detected a problem with your graph: - |At the following node, the number of inward ports should equal the number of produced inward parameters. - |$context - |$connectedPortsInfo - |Upstreamed outward parameters: [${uoParams.mkString(",")}] - |Produced inward parameters: [${i.mkString(",")}] - |""".stripMargin) - i.map(inner.mixI(_, this)) - } catch { - case c: UpwardCycleException => throw c.copy(loop = context +: c.loop) - } - } - - /** Outward edge parameters. */ - protected[diplomacy] lazy val edgesOut: Seq[EO] = (oPorts zip doParams).map { case ((i, n, p, s), o) => outer.edgeO(o, n.uiParams(i), p, s) } - - /** Inward edge parameters. */ - protected[diplomacy] lazy val edgesIn: Seq[EI] = (iPorts zip uiParams).map { case ((o, n, p, s), i) => inner.edgeI(n.doParams(o), i, p, s) } - - /** A tuple of the input edge parameters and output edge parameters for the edges bound to this node. - * - * If you need to access to the edges of a foreign Node, use this method (in/out create bundles). - */ - lazy val edges: Edges[EI, EO] = Edges(edgesIn, edgesOut) - - /** Create actual Wires corresponding to the Bundles parameterized by the outward edges of this node. */ - protected[diplomacy] lazy val bundleOut: Seq[BO] = edgesOut.map { e => - val x = Wire(outer.bundleO(e)).suggestName(s"${valName.name}Out") - // TODO: Don't care unconnected forwarded diplomatic signals for compatibility issue, - // In the future, we should add an option to decide whether allowing unconnected in the LazyModule - x := DontCare - x - } - - /** Create actual Wires corresponding to the Bundles parameterized by the inward edges of this node. */ - protected[diplomacy] lazy val bundleIn: Seq[BI] = edgesIn .map { e => - val x = Wire(inner.bundleI(e)).suggestName(s"${valName.name}In") - // TODO: Don't care unconnected forwarded diplomatic signals for compatibility issue, - // In the future, we should add an option to decide whether allowing unconnected in the LazyModule - x := DontCare - x - } - - private def emptyDanglesOut: Seq[Dangle] = oPorts.zipWithIndex.map { case ((j, n, _, _), i) => - Dangle( - source = HalfEdge(serial, i), - sink = HalfEdge(n.serial, j), - flipped= false, - name = wirePrefix + "out", - dataOpt= None) - } - private def emptyDanglesIn: Seq[Dangle] = iPorts.zipWithIndex.map { case ((j, n, _, _), i) => - Dangle( - source = HalfEdge(n.serial, j), - sink = HalfEdge(serial, i), - flipped= true, - name = wirePrefix + "in", - dataOpt=None) - } - - - /** Create the [[Dangle]]s which describe the connections from this node output to other nodes inputs. */ - protected[diplomacy] def danglesOut: Seq[Dangle] = emptyDanglesOut.zipWithIndex.map { - case (d,i) => d.copy(dataOpt = Some(bundleOut(i))) - } - - /** Create the [[Dangle]]s which describe the connections from this node input from other nodes outputs. */ - protected[diplomacy] def danglesIn: Seq[Dangle] = emptyDanglesIn.zipWithIndex.map { - case (d,i) => d.copy(dataOpt = Some(bundleIn(i))) - } - - private[diplomacy] var instantiated = false - - /** Gather Bundle and edge parameters of outward ports. - * - * Accessors to the result of negotiation to be used within - * [[LazyModuleImp]] Code. Should only be used within [[LazyModuleImp]] code - * or after its instantiation has completed. - */ - def out: Seq[(BO, EO)] = { - require(instantiated, s"$name.out should not be called until after instantiation of its parent LazyModule.module has begun") - bundleOut zip edgesOut - } - - /** Gather Bundle and edge parameters of inward ports. - * - * Accessors to the result of negotiation to be used within - * [[LazyModuleImp]] Code. Should only be used within [[LazyModuleImp]] code - * or after its instantiation has completed. - */ - def in: Seq[(BI, EI)] = { - require(instantiated, s"$name.in should not be called until after instantiation of its parent LazyModule.module has begun") - bundleIn zip edgesIn - } - - /** Actually instantiate this node during [[LazyModuleImp]] evaluation. - * Mark that it's safe to use the Bundle wires, - * instantiate monitors on all input ports if appropriate, - * and return all the dangles of this node. - */ - protected[diplomacy] def instantiate(): Seq[Dangle] = { - instantiated = true - if (!circuitIdentity) { - (iPorts zip in) foreach { - case ((_, _, p, _), (b, e)) => if (p(MonitorsEnabled)) inner.monitor(b, e) - } } - danglesOut ++ danglesIn - } - - protected[diplomacy] def cloneDangles(): Seq[Dangle] = emptyDanglesOut ++ emptyDanglesIn - - /** Connects the outward part of a node with the inward part of this node. */ - protected[diplomacy] def bind(h: OutwardNode[DI, UI, BI], binding: NodeBinding)(implicit p: Parameters, sourceInfo: SourceInfo): Unit = { - val x = this // x := y - val y = h - val info = sourceLine(sourceInfo, " at ", "") - val i = x.iPushed - val o = y.oPushed - y.oPush(i, x, binding match { - case BIND_ONCE => BIND_ONCE - case BIND_FLEX => BIND_FLEX - case BIND_STAR => BIND_QUERY - case BIND_QUERY => BIND_STAR - }) - x.iPush(o, y, binding) - } - - /* Metadata for printing the node graph. */ - def inputs: Seq[(OutwardNode[DI, UI, BI], RenderedEdge)] = (iPorts zip edgesIn) map { case ((_, n, p, _), e) => - val re = inner.render(e) - (n, re.copy(flipped = re.flipped != p(RenderFlipped))) - } - /** Metadata for printing the node graph */ - def outputs: Seq[(InwardNode[DO, UO, BO], RenderedEdge)] = oPorts map { case (i, n, _, _) => (n, n.inputs(i)._2) } -} - -/** A [[MixedNode]] that may be extended with custom behavior. */ -abstract class MixedCustomNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( - inner: InwardNodeImp [DI, UI, EI, BI], - outer: OutwardNodeImp[DO, UO, EO, BO])( - implicit valName: ValName) - extends MixedNode(inner, outer) { - override def description = "custom" - def resolveStar(iKnown: Int, oKnown: Int, iStars: Int, oStars: Int): (Int, Int) - def mapParamsD(n: Int, p: Seq[DI]): Seq[DO] - def mapParamsU(n: Int, p: Seq[UO]): Seq[UI] -} - -/** A [[NodeImp]] that may be extended with custom behavior. - * - * Different from a [[MixedNode]] in that the inner and outer [[NodeImp]]s are the same. - */ -abstract class CustomNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])( - implicit valName: ValName) - extends MixedCustomNode(imp, imp) - -/** A JunctionNode creates multiple parallel arbiters. - * - * @example - * {{{ - * val jbar = LazyModule(new JBar) - * slave1.node := jbar.node - * slave2.node := jbar.node - * extras.node :=* jbar.node - * jbar.node :*= masters1.node - * jbar.node :*= masters2.node - * }}} - * - * In the above example, only the first two connections have their multiplicity specified. - * All the other connections include a '*' on the JBar's side, so the JBar decides the multiplicity. - * Thus, in this example, we get 2x crossbars with 2 masters like this: - * {slave1, extras.1} <= jbar.1 <= {masters1.1, masters2.1} - * {slave2, extras.2} <= jbar.2 <= {masters1.2, masters2,2} - * - * @example - * {{{ - * val jbar = LazyModule(new JBar) - * jbar.node :=* masters.node - * slaves1.node :=* jbar.node - * slaves2.node :=* jbar.node - * }}} - * In the above example, the first connection takes multiplicity (*) from the right (masters). - * Supposing masters.node had 3 edges, this would result in these three arbiters: - * {slaves1.1, slaves2.1} <= jbar.1 <= { masters.1 } - * {slaves1.2, slaves2.2} <= jbar.2 <= { masters.2 } - * {slaves1.3, slaves2.3} <= jbar.3 <= { masters.3 } - */ -class MixedJunctionNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( - inner: InwardNodeImp [DI, UI, EI, BI], - outer: OutwardNodeImp[DO, UO, EO, BO])( - dFn: Seq[DI] => Seq[DO], - uFn: Seq[UO] => Seq[UI])( - implicit valName: ValName) - extends MixedNode(inner, outer) { - protected[diplomacy] var multiplicity = 0 - - def uRatio: Int = iPorts.size / multiplicity - def dRatio: Int = oPorts.size / multiplicity - - override def description = "junction" - protected[diplomacy] def resolveStar(iKnown: Int, oKnown: Int, iStars: Int, oStars: Int): (Int, Int) = { - require (iKnown == 0 || oKnown == 0, - s"""Diplomacy has detected a problem with your graph: - |The following node appears left of a :=* or a := and right of a :*= or :=. Only one side may drive multiplicity. - |$context - |$bindingInfo - |""".stripMargin) - multiplicity = iKnown max oKnown - (multiplicity, multiplicity) - } - protected[diplomacy] def mapParamsD(n: Int, p: Seq[DI]): Seq[DO] = - p.grouped(multiplicity).toList.transpose.map(dFn).transpose.flatten - protected[diplomacy] def mapParamsU(n: Int, p: Seq[UO]): Seq[UI] = - p.grouped(multiplicity).toList.transpose.map(uFn).transpose.flatten - - def inoutGrouped: Seq[(Seq[(BI, EI)], Seq[(BO, EO)])] = { - val iGroups = in .grouped(multiplicity).toList.transpose - val oGroups = out.grouped(multiplicity).toList.transpose - iGroups zip oGroups - } -} - -/** A node type which has a fixed ratio between the number of input edges and output edges. - * - * The [[NodeImp]] on either side is the same. - * - * One example usage would be for putting down a series of 2:1 arbiters. - * - * Suppose you had N banks of L2 and wanted to connect those to two different driver crossbars. - * In that case you can do this: - * {{{ - * l2banks.node :*= jbar.node - * jbar.node :*= xbar1.node - * jbar.node :*= xbar2.node - * }}} - * If the L2 has 4 banks, now there are 4 egress ports on both xbar1 and xbar2 and they are arbitrated by the jbar. - */ -class JunctionNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])( - dFn: Seq[D] => Seq[D], - uFn: Seq[U] => Seq[U])( - implicit valName: ValName) - extends MixedJunctionNode[D, U, EI, B, D, U, EO, B](imp, imp)(dFn, uFn) - -/** [[MixedAdapterNode]] is used to transform between different diplomacy protocols ([[NodeImp]]), without changing the number of edges passing through it. - * - * For example, a [[MixedAdapterNode]] is needed for a TL to AXI bridge (interface). - * {{{ - * case class TLToAXI4Node(stripBits: Int = 0)(implicit valName: ValName) extends MixedAdapterNode(TLImp, AXI4Imp) - * }}} - * - * @param dFn convert downward parameter from input to output. - * @param uFn convert upward parameter from output to input. - */ -class MixedAdapterNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( - inner: InwardNodeImp [DI, UI, EI, BI], - outer: OutwardNodeImp[DO, UO, EO, BO])( - dFn: DI => DO, - uFn: UO => UI)( - implicit valName: ValName) - extends MixedNode(inner, outer) { - override def description = "adapter" - protected[diplomacy] override def flexibleArityDirection = true - protected[diplomacy] def resolveStar(iKnown: Int, oKnown: Int, iStars: Int, oStars: Int): (Int, Int) = { - require (oStars + iStars <= 1, - s"""Diplomacy has detected a problem with your graph: - |The following node appears left of a :*= $iStars times and right of a :=* $oStars times, at most once is allowed. - |$context - |$bindingInfo - |""".stripMargin) - if (oStars > 0) { - require (iKnown >= oKnown, - s"""Diplomacy has detected a problem with your graph: - |After being connected right of :=*, the following node appears left of a := $iKnown times and right of a := $oKnown times. - |${iKnown - oKnown} additional right of := bindings are required to resolve :=* successfully. - |$context - |$bindingInfo - |""".stripMargin) - (0, iKnown - oKnown) - } else if (iStars > 0) { - require (oKnown >= iKnown, - s"""Diplomacy has detected a problem with your graph: - |After being connected left of :*=, the following node appears left of a := $iKnown times and right of a := $oKnown times. - |${oKnown - iKnown} additional left := bindings are required to resolve :*= successfully. - |$context - |$bindingInfo - |""".stripMargin) - (oKnown - iKnown, 0) - } else { - require (oKnown == iKnown, - s"""Diplomacy has detected a problem with your graph: - |The following node appears left of a := $iKnown times and right of a := $oKnown times. - |Either the number of bindings on both sides of the node match, or connect this node by left-hand side of :*= or right-hand side of :=* - |$context - |$bindingInfo - |""".stripMargin) - (0, 0) - } - } - protected[diplomacy] def mapParamsD(n: Int, p: Seq[DI]): Seq[DO] = { - require(n == p.size, - s"""Diplomacy has detected a problem with your graph: - |The following node has ${p.size} inputs and $n outputs, they must match. - |$context - |$bindingInfo - |""".stripMargin) - p.map(dFn) - } - protected[diplomacy] def mapParamsU(n: Int, p: Seq[UO]): Seq[UI] = { - require(n == p.size, - s"""Diplomacy has detected a problem with your graph: - |The following node has $n inputs and ${p.size} outputs, they must match - |$context - |$bindingInfo - |""".stripMargin) - p.map(uFn) - } -} - -/** A node which modifies the parameters flowing through it, but without changing the number of edges or the diplomatic protocol implementation. */ -class AdapterNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])( - dFn: D => D, - uFn: U => U)( - implicit valName: ValName) - extends MixedAdapterNode[D, U, EI, B, D, U, EO, B](imp, imp)(dFn, uFn) - -/** A node which does not modify the parameters nor the protocol for edges that pass through it. - * - * During hardware generation, [[IdentityNode]]s automatically connect their inputs to outputs. - */ -class IdentityNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])()(implicit valName: ValName) - extends AdapterNode(imp)({ s => s }, { s => s }) { - override def description = "identity" - override final def circuitIdentity = true - override protected[diplomacy] def instantiate(): Seq[Dangle] = { - val dangles = super.instantiate() - (out zip in) foreach { case ((o, _), (i, _)) => o :<>= i } - dangles - } - override protected[diplomacy] def cloneDangles(): Seq[Dangle] = super.cloneDangles() -} - -/** [[EphemeralNode]]s are used as temporary connectivity placeholders, but disappear from the final node graph. - * An ephemeral node provides a mechanism to directly connect two nodes to each other where neither node knows about the other, - * but both know about an ephemeral node they can use to facilitate the connection. - */ -class EphemeralNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])()(implicit valName: ValName) - extends AdapterNode(imp)({ s => s }, { s => s }) { - override def description = "ephemeral" - override final def circuitIdentity = true - override def omitGraphML = true - override def oForward(x: Int): Option[(Int, OutwardNode[D, U, B])] = Some(iDirectPorts(x) match { case (i, n, _, _) => (i, n) }) - override def iForward(x: Int): Option[(Int, InwardNode[D, U, B])] = Some(oDirectPorts(x) match { case (i, n, _, _) => (i, n) }) - override protected[diplomacy] def instantiate(): Seq[Dangle] = { - instantiated = true - Nil - } - override protected[diplomacy] def cloneDangles(): Seq[Dangle] = Nil -} - -/** [[MixedNexusNode]] is used when the number of nodes connecting from either side is unknown (e.g. a Crossbar which also is a protocol adapter). - * - * The [[NodeImp]] is different between [[inner]] and [[outer]], - * - * @param dFn Function for mapping the parameters flowing downward into new outward flowing down parameters. - * @param uFn Function for mapping the parameters flowing upward into new inward flowing up parameters. - * @param inputRequiresOutput True if it is required that if there are input connections, there are output connections (this node can't just be a sink). - * @param outputRequiresInput True if it is required that if there are output connections, there are input connections (this node can't just be a source). - */ -class MixedNexusNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( - inner: InwardNodeImp [DI, UI, EI, BI], - outer: OutwardNodeImp[DO, UO, EO, BO])( - dFn: Seq[DI] => DO, - uFn: Seq[UO] => UI, - // no inputs and no outputs is always allowed - inputRequiresOutput: Boolean = true, - outputRequiresInput: Boolean = true)( - implicit valName: ValName) - extends MixedNode(inner, outer) -{ - override def description = "nexus" - protected[diplomacy] def resolveStar(iKnown: Int, oKnown: Int, iStars: Int, oStars: Int): (Int, Int) = { - // a nexus treats :=* as a weak pointer - def resolveStarInfo: String = - s"""$context - |$bindingInfo - |number of known := bindings to inward nodes: $iKnown - |number of known := bindings to outward nodes: $oKnown - |number of binding queries from inward nodes: $iStars - |number of binding queries from outward nodes: $oStars - |""".stripMargin - require(!outputRequiresInput || oKnown == 0 || iStars + iKnown != 0, - s"""Diplomacy has detected a problem with your graph: - |The following node has $oKnown outward connections and no inward connections. At least one inward connection was required. - |$resolveStarInfo - |""".stripMargin) - require(!inputRequiresOutput || iKnown == 0 || oStars + oKnown != 0, - s"""Diplomacy has detected a problem with your graph: - |The following node node has $iKnown inward connections and no outward connections. At least one outward connection was required. - |$resolveStarInfo - |""".stripMargin) - if (iKnown == 0 && oKnown == 0) (0, 0) else (1, 1) - } - protected[diplomacy] def mapParamsD(n: Int, p: Seq[DI]): Seq[DO] = { if (n > 0) { val a = dFn(p); Seq.fill(n)(a) } else Nil } - protected[diplomacy] def mapParamsU(n: Int, p: Seq[UO]): Seq[UI] = { if (n > 0) { val a = uFn(p); Seq.fill(n)(a) } else Nil } -} - -/** [[NexusNode]] is a [[MixedNexusNode]], in which the inward and outward side of the node have the same [[NodeImp]] implementation. */ -class NexusNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])( - dFn: Seq[D] => D, - uFn: Seq[U] => U, - inputRequiresOutput: Boolean = true, - outputRequiresInput: Boolean = true)( - implicit valName: ValName) - extends MixedNexusNode[D, U, EI, B, D, U, EO, B](imp, imp)(dFn, uFn, inputRequiresOutput, outputRequiresInput) - -/** A node which represents a node in the graph which only has outward edges and no inward edges. - * - * A [[SourceNode]] cannot appear left of a `:=`, `:*=`, `:=*, or `:*=*` - * There are no Mixed [[SourceNode]]s, There are no "Mixed" [[SourceNode]]s because each one only has an outward side. - */ -class SourceNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(po: Seq[D])(implicit valName: ValName) - extends MixedNode(imp, imp) -{ - override def description = "source" - protected[diplomacy] def resolveStar(iKnown: Int, oKnown: Int, iStars: Int, oStars: Int): (Int, Int) = { - def resolveStarInfo: String = - s"""$context - |$bindingInfo - |number of known := bindings to inward nodes: $iKnown - |number of known := bindings to outward nodes: $oKnown - |number of binding queries from inward nodes: $iStars - |number of binding queries from outward nodes: $oStars - |${po.size} outward parameters: [${po.map(_.toString).mkString(",")}] - |""".stripMargin - require(oStars <= 1, - s"""Diplomacy has detected a problem with your graph: - |The following node appears right of a :=* $oStars times; at most once is allowed. - |$resolveStarInfo - |""".stripMargin) - require(iStars == 0, - s"""Diplomacy has detected a problem with your graph: - |The following node cannot appear left of a :*= - |$resolveStarInfo - |""".stripMargin) - require(iKnown == 0, - s"""Diplomacy has detected a problem with your graph: - |The following node cannot appear left of a := - |$resolveStarInfo - |""".stripMargin) - if (oStars == 0) - require(po.size == oKnown, - s"""Diplomacy has detected a problem with your graph: - |The following node has $oKnown outward bindings connected to it, but ${po.size} sources were specified to the node constructor. - |Either the number of outward := bindings should be exactly equal to the number of sources, or connect this node on the right-hand side of a :=* - |$resolveStarInfo - |""".stripMargin) - else - require(po.size >= oKnown, - s"""Diplomacy has detected a problem with your graph: - |The following node has $oKnown outward bindings connected to it, but ${po.size} sources were specified to the node constructor. - |To resolve :=*, size of outward parameters can not be less than bindings. - |$resolveStarInfo - |""".stripMargin - ) - (0, po.size - oKnown) - } - protected[diplomacy] def mapParamsD(n: Int, p: Seq[D]): Seq[D] = po - protected[diplomacy] def mapParamsU(n: Int, p: Seq[U]): Seq[U] = Seq() - - def makeIOs()(implicit valName: ValName): HeterogeneousBag[B] = { - val bundles = this.out.map(_._1) - val ios = IO(Flipped(new HeterogeneousBag(bundles))) - ios.suggestName(valName.name) - bundles.zip(ios).foreach { case (bundle, io) => bundle <> io } - ios - } -} - -/** A node which represents a node in the graph which has only inward edges, no outward edges. - * - * A [[SinkNode]] cannot appear cannot appear right of a `:=`, `:*=`, `:=*`, or `:*=*` - * - * There are no "Mixed" [[SinkNode]]s because each one only has an inward side. - */ -class SinkNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(pi: Seq[U])(implicit valName: ValName) - extends MixedNode(imp, imp) -{ - override def description = "sink" - protected[diplomacy] def resolveStar(iKnown: Int, oKnown: Int, iStars: Int, oStars: Int): (Int, Int) = { - def resolveStarInfo: String = - s"""$context - |$bindingInfo - |number of known := bindings to inward nodes: $iKnown - |number of known := bindings to outward nodes: $oKnown - |number of binding queries from inward nodes: $iStars - |number of binding queries from outward nodes: $oStars - |${pi.size} inward parameters: [${pi.map(_.toString).mkString(",")}] - |""".stripMargin - require (iStars <= 1, - s"""Diplomacy has detected a problem with your graph: - |The following node appears left of a :*= $iStars times; at most once is allowed. - |$resolveStarInfo - |""".stripMargin) - require (oStars == 0, - s"""Diplomacy has detected a problem with your graph: - |The following node cannot appear right of a :=* - |$resolveStarInfo - |""".stripMargin) - require (oKnown == 0, - s"""Diplomacy has detected a problem with your graph: - |The following node cannot appear right of a := - |$resolveStarInfo - |""".stripMargin) - if (iStars == 0) - require(pi.size == iKnown, - s"""Diplomacy has detected a problem with your graph: - |The following node has $iKnown inward bindings connected to it, but ${pi.size} sinks were specified to the node constructor. - |Either the number of inward := bindings should be exactly equal to the number of sink, or connect this node on the left-hand side of a :*= - |$resolveStarInfo - |""".stripMargin) - else - require(pi.size >= iKnown, - s"""Diplomacy has detected a problem with your graph: - |The following node has $iKnown inward bindings connected to it, but ${pi.size} sinks were specified to the node constructor. - |To resolve :*=, size of inward parameters can not be less than bindings. - |$resolveStarInfo - |""".stripMargin - ) - (pi.size - iKnown, 0) - } - protected[diplomacy] def mapParamsD(n: Int, p: Seq[D]): Seq[D] = Seq() - protected[diplomacy] def mapParamsU(n: Int, p: Seq[U]): Seq[U] = pi - - def makeIOs()(implicit valName: ValName): HeterogeneousBag[B] = { - val bundles = this.in.map(_._1) - val ios = IO(new HeterogeneousBag(bundles)) - ios.suggestName(valName.name) - bundles.zip(ios).foreach { case (bundle, io) => io <> bundle } - ios - } -} - -/** A node intended to replace a portion of the diplomatic graph in order to test functionality of a copy (cloned) [[LazyModule]] - * - * @param node the node to copy - * @param clone the copy of the LazyModule containing [[node]] - */ -class MixedTestNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data] protected[diplomacy]( - node: NodeHandle [DI, UI, EI, BI, DO, UO, EO, BO], clone: CloneLazyModule)( - implicit valName: ValName) - extends MixedNode(node.inner, node.outer) -{ - // The devices connected to this test node must recreate these parameters: - def iParams: Seq[DI] = node.inward .diParams - def oParams: Seq[UO] = node.outward.uoParams - - override def description = "test" - protected[diplomacy] def resolveStar(iKnown: Int, oKnown: Int, iStars: Int, oStars: Int): (Int, Int) = { - def resolveStarInfo: String = - s"""$context - |$bindingInfo - |number of known := bindings to inward nodes: $iKnown - |number of known := bindings to outward nodes: $oKnown - |number of binding queries from inward nodes: $iStars - |number of binding queries from outward nodes: $oStars - |downstream inward parameters: ${node.inward.diParams} - |upstream inward parameters: ${node.inward.uiParams} - |upstream outward parameters: ${node.outward.uoParams} - |downstream outward parameters: ${node.outward.doParams} - |node.inward.uiParams.size - |""".stripMargin - - require(oStars <= 1, - s"""Diplomacy has detected a problem with your graph: - |The following node appears right of a :=* $oStars times; at most once is allowed. - |$resolveStarInfo - |""".stripMargin) - require(iStars <= 1, - s"""Diplomacy has detected a problem with your graph: - |The following node appears left of a :*= $iStars times; at most once is allowed. - |$resolveStarInfo - |""".stripMargin) - require(node.inward .uiParams.size == iKnown || iStars == 1, - s"""Diplomacy has detected a problem with your graph: - |The following node has only $iKnown inputs, which should be ${node.inward.uiParams.size}, or connect this node on the left-hand side of :*= - |$resolveStarInfo - |""".stripMargin) - require(node.outward.doParams.size == oKnown || oStars == 1, - s"""Diplomacy has detected a problem with your graph: - |The following node has only $oKnown outputs, which should be ${node.outward.doParams.size}, or connect this node on the right-hand side of :=* - |$resolveStarInfo - |""".stripMargin) - (node.inward.uiParams.size - iKnown, node.outward.doParams.size - oKnown) - } - - protected[diplomacy] def mapParamsU(n: Int, p: Seq[UO]): Seq[UI] = node.inward .uiParams - protected[diplomacy] def mapParamsD(n: Int, p: Seq[DI]): Seq[DO] = node.outward.doParams - - override protected[diplomacy] def instantiate(): Seq[Dangle] = { - val dangles = super.instantiate() - val orig_module = clone.base.module - val clone_auto = clone.io("auto").asInstanceOf[AutoBundle] - - danglesOut.zipWithIndex.foreach { case (d, i) => - val orig = orig_module.dangles.find(_.source == HalfEdge(node.outward.serial, i)) - require (orig.isDefined, s"Cloned node ${node.outward.name} must be connected externally out ${orig_module.name}") - val io_name = orig_module.auto.elements.find(_._2 eq orig.get.data).get._1 - d.data <> clone_auto.elements(io_name) - } - danglesIn.zipWithIndex.foreach { case (d, i) => - val orig = orig_module.dangles.find(_.sink == HalfEdge(node.inward.serial, i)) - require (orig.isDefined, s"Cloned node ${node.inward.name} must be connected externally in ${orig_module.name}") - val io_name = orig_module.auto.elements.find(_._2 eq orig.get.data).get._1 - clone_auto.elements(io_name) <> d.data - } - - dangles - } - override protected[diplomacy] def cloneDangles(): Seq[Dangle] = { - require(false, "Unsupported") - super.cloneDangles() - } -} diff --git a/src/main/scala/diplomacy/Resources.scala b/src/main/scala/diplomacy/Resources.scala index 4dc59d9530..78a23ae188 100644 --- a/src/main/scala/diplomacy/Resources.scala +++ b/src/main/scala/diplomacy/Resources.scala @@ -280,7 +280,7 @@ trait BindingScope private case class ExpandedValue(path: Seq[String], labels: Seq[String], value: Seq[ResourceValue]) private lazy val eval: Unit = { - require (!LazyModule.scope.isDefined, "May not evaluate binding while still constructing LazyModules") + require (!LazyModule.getScope.isDefined, "May not evaluate binding while still constructing LazyModules") parentScope.foreach { _.eval } resourceBindings = parentScope.map(_.resourceBindings).getOrElse(Nil) BindingScope.active = Some(this) @@ -388,9 +388,9 @@ trait BindingScope object BindingScope { protected[diplomacy] var active: Option[BindingScope] = None - protected[diplomacy] def find(m: Option[LazyModule] = LazyModule.scope): Option[BindingScope] = m.flatMap { - case x: BindingScope => find(x.parent).orElse(Some(x)) - case x => find(x.parent) + protected[diplomacy] def find(m: Option[LazyModule] = LazyModule.getScope): Option[BindingScope] = m.flatMap { + case x: BindingScope => find(x.getParent).orElse(Some(x)) + case x => find(x.getParent) } var bindingScopes = new collection.mutable.ArrayBuffer[BindingScope]() diff --git a/src/main/scala/diplomacy/ValName.scala b/src/main/scala/diplomacy/ValName.scala deleted file mode 100644 index bf0e581229..0000000000 --- a/src/main/scala/diplomacy/ValName.scala +++ /dev/null @@ -1,12 +0,0 @@ -// See LICENSE.SiFive for license details. - -package freechips.rocketchip.diplomacy - -import freechips.rocketchip.macros.ValNameImpl - -case class ValName(name: String) - -object ValName -{ - implicit def materialize(implicit x: ValNameImpl): ValName = ValName(x.name) -} diff --git a/src/main/scala/diplomacy/package.scala b/src/main/scala/diplomacy/package.scala index 5433147fae..0453f7c924 100644 --- a/src/main/scala/diplomacy/package.scala +++ b/src/main/scala/diplomacy/package.scala @@ -2,190 +2,15 @@ package freechips.rocketchip -import chisel3.experimental.{SourceInfo, SourceLine} import chisel3.Data +import chisel3.experimental.{SourceInfo, SourceLine} import org.chipsalliance.cde.config.Parameters -import scala.language.implicitConversions +import scala.language.implicitConversions -/** Diplomacy is a set of abstractions for describing directed, acyclic graphs - * where parameters will be negotiated between nodes. These abstractions are - * expressed in the form of abstract classes, traits, and type parameters, which - * comprises nearly all of the types defined in this package. - * - * The [[NodeImp]] ("node implementation") is the main abstract type that associates - * the type parameters of all other abstract types. Defining a concrete - * implementation of [[NodeImp]] will therefore determine concrete types for all - * type parameters. For example, passing in a concrete instance of NodeImp to a - * SourceNode will fully determine concrete types for all of a SourceNode's type - * parameters. - * - * Specific applications of Diplomacy are expected to either extend these types - * or to specify concrete types for the type parameters. This allows for - * creating and associating application-specific node, edge, parameter, and bundle types. - * - * - * =Concepts, metaphors, and mnemonics to help with understanding Diplomacy code= - * - * ==Parameter Types== - * - * There are several types of parameters involved in diplomacy code. - * - * - Upward-flowing/Downward-flowing Parameters: These parameter types flow along edges and can be considered as the - * pre-negotiated, unresolved parameters. - * - Edge Parameters: These parameters are the result of the diplomatic negotiation and that is resolved for each edge. - * They are metadata, or an abstract concept of the connection represented by their edge, and contain any sort. - * These are an abstract concept which carry any sort of conceptual information that is useful to pass along the graph edge. - * For example, the full address map visible from a given edge and the supported access types for each memory region. - * - "p" Parameters: These are parameters of type [[Parameters]] which are implicit and generally available - * in various stages of the diplomacy codebase. - * Often they are captured from the context in which a particular diplomatic node or edge is created or bound. - * - Bundle Parameters: These parameters are used for actual hardware generation of the [[chisel3.Data]]s which connect - * diplomatic components. In contrast to edge parameters, this may carry information like the width of an address - * or opcode field. - * While they are derived from Edge parameters holding all metadata computed over an edge, - * Bundle parameters often contain only concrete information required to create the hardware type, - * such as [[freechips.rocketchip.tilelink.TLBundleParameters]] and [[freechips.rocketchip.amba.axi4.AXI4BundleParameters]] - * - * ==Inward/Outward vs. Upward/Downward== - * - * Diplomacy defines two dimensions: inward/outward and upward/downward. - * - * Inward/outward refer to the direction of edges from the perspective of a given node. - * For a given node: - * - Inward refers to edges that point into itself. - * - Outward refers to edges that point out from itself. - * - * Therefore, a given edge is always described as inward to one node and as outward from another. - * - * Upward/downward refer to the direction of the overall directed acyclic graph. - * Because each each edge is directed, we say that the directions flow from sources (nodes that only have outward edges) - * downwards to sinks (nodes that only have inward edges), or from sinks upwards to sources. - * These terms are used in parameter negotiation, where parameters flow both - * upwards and downwards on edges. Note that diplomacy avoids terms like "master/slave", - * "producer/consumer", though these can be defined by the concrete implementations of diplomatic systems. - * Such terms imply something about the transactional behavior of agents within a protocol, - * whereas source/sink and up/down refer only to the structure of the graph and the flow of parameters. - * - Upward refers to the flow of parameters along edges in the upwards direction. - * - Downward refers to a flow of parameters along edges in the downwards direction. - * - * A useful mnemonic for distinguishing between upward and downward is to imagine - * a diplomatic graph as a literal network of rivers where water flows in the direction of the edges, - * and parameters that move in the upstream direction, - * while downward refers to parameters that move in the downstream direction. - * - * ==Acronyms== - * - * Diplomacy has some commonly used acronyms described below: - * D[IO], U[IO], E[IO], B[IO] are the types of parameters which will be propagated. - * D: Downwards -- parameters passed in the same direction as the edge. - * U: Upwards -- parameters passed in the opposite direction as the edge. - * E: Edge -- resolved (negotiated) parameters describing conceptual information on graph edges. - * B: Bundle should extends from [[chisel3.Data]]. - * - * {{{ - * - * - * Upwards (a.k.a. towards Sources) ↓ - * ↓ - * inward edge of (parameter) type EI ↓ - * created from parameters of type UI and DI ↓ - * will result in a Bundle of type BI ↓ - * ↓ - * ^ ↓ * - * . ↓ * - * ┌───────────────────────────────.──↓──*───────────────────────────────┐ - * │ . ↓ * BaseNode │ - * │ . ↓ * (NodeImp) │ - * │ . ↓ * │ - * │ ┌────────────────────────────.──↓──*────────────────────────────┐ │ - * │ │ . ↓ * InwardNode (InwardNodeImp)│ │ - * │ │ (mixI)↓ * │ │ - * │ │ . ↓ * │ │ - * │ │ Upward-flowing inwards . ↓ * Downward-Flowing inwards │ │ - * │ │ parameters of type UI . ↓ * parameters of type DI │ │ - * │ └────────────────────────────.──↓──*────────────────────────────┘ │ - * │ . ↓ * │ - * │ . I v │ - * │ (mapParamsU) (mapParamsD) │ - * │ ^ O + │ - * │ : ↓ + │ - * │ ┌────────────────────────────:──↓──+────────────────────────────┐ │ - * │ │ : ↓ + OutwardNode(OutwardNodeImp)│ │ - * │ │ : ↓ (mixO) │ │ - * │ │ : ↓ + │ │ - * │ │ Upward-flowing outwards : ↓ + Downward-Flowing outward │ │ - * │ │ parameters of type UO : ↓ + parameters of type DO │ │ - * │ └────────────────────────────:──↓──+────────────────────────────┘ │ - * │ : ↓ + │ - * │ : ↓ + │ - * └───────────────────────────────.──↓──*───────────────────────────────┘ - * : ↓ * - * : ↓ v - * ↓ outward edge of (parameter) type EO - * ↓ created from parameters of type UO and DO - * ↓ will result in a Bundle of type BO - * ↓ - * ↓ Downwards (a.k.a. towards Sinks) - * - * }}} - * - * == Handles == - * - * Two Diplomatic nodes can be bound together using the `:=` operator or one of - * its sibling operators. Binding is asymmetric, and the binding operation will - * connect the outward of one node to the inward of the other. - * - * For example, the expression `a := b` will connect the outward of `b` to the - * inward of `a`. - * - * We would like the `:=` operator to have additional properties that make it - * intuitive to use: - * - * 1. It should be chainable, so that `a := b := c` will have the intuitive effect - * of binding c to b and b to a. This requires that the return type of `:=` be the - * same as its arguments, because the result of one `:=` operation must be - * valid as an argument to other `:=` operations. - * - * 2. It should be associative, so that `(a := b) := c` is equivalent to `a := (b := c).` - * This means that the order in which the bind operations execute does - * not matter, even if split across multiple files. - * - * 3. `a := b` should only be allowed if and only if `b` allows outward edges and `a` - * allows inward edges. This should be preserved even when chaining - * operations, and it should ideally be enforced at compile time. - * - * [[NodeHandle]] are a way of satisfying all of these properties. A Handle represents - * the aggregation of a chain of Nodes, and it preserves information about - * the connectability of the innermost and the outermost sides of the chain. - * - * If `b` supports inward edges, then `a := b` returns a [[NodeHandle]] that supports inward - * edges that go into `b`. If `a` supports outward edges, then `a := b` returns a - * [[NodeHandle]] that supports outward edges coming out of `a`. - * - * ==Node Terms== - * - * These are some conventionally used terms for diplomatic Nodes, - * which describe different common subtypes that certain protocol implementation might utilize: - * - Mixed: implies that the inward and outward NodeImp are not the same (some sort of protocol conversion is occurring between the two implementations). - * - Adapter: the number of inward and outward edges must be the same. - * - Nexus: the number of nodes connecting from either side are unknown until the graph is constructed and can differ from one another. - * - Identity: modifies neither the parameters nor the protocol-specific circuitry for the edges that pass through it. - * - Source: cannot have inward edges. - * - Sink: cannot have outward edges. - * - Junction: the number of inward and outward edges must have a fixed ratio to one another - * - Ephemeral: a temporary placeholder used for connectivity operations +/** Rocketchip Specific Diplomacy code. All other Diplomacy core functionality has been moved to standalone diplomacy */ -package object diplomacy -{ - type SimpleNodeHandle[D, U, E, B <: Data] = NodeHandle[D, U, E, B, D, U, E, B] - type AnyMixedNode = MixedNode[_, _, _, _ <: Data, _, _, _, _ <: Data] - - def sourceLine(sourceInfo: SourceInfo, prefix: String = " (", suffix: String = ")") = sourceInfo match { - case SourceLine(filename, line, col) => s"$prefix$filename:$line:$col$suffix" - case _ => "" - } - +package object diplomacy { def bitIndexes(x: BigInt, tail: Seq[Int] = Nil): Seq[Int] = { require (x >= 0) if (x == 0) { @@ -222,21 +47,230 @@ package object diplomacy def asProperty: Seq[ResourceValue] = Seq(ResourceReference(x.label)) } - def EnableMonitors[T](body: Parameters => T)(implicit p: Parameters) = body(p.alterPartial { - case MonitorsEnabled => true - }) - def DisableMonitors[T](body: Parameters => T)(implicit p: Parameters) = body(p.alterPartial { - case MonitorsEnabled => false - }) - def FlipRendering[T](body: Parameters => T)(implicit p: Parameters) = body(p.alterPartial { - case RenderFlipped => !p(RenderFlipped) - }) + implicit def noCrossing(value: NoCrossing.type): ClockCrossingType = SynchronousCrossing(BufferParams.none) + + // TODO - Remove compatibility layer for deprecated diplomacy api once all local references are moved to standalone diplomacy lib. + // package.scala + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + def sourceLine(sourceInfo: SourceInfo, prefix: String = " (", suffix: String = ")") = sourceInfo match { + case SourceLine(filename, line, col) => s"$prefix$filename:$line:$col$suffix" + case _ => "" + } + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + def EnableMonitors[T]( + body: Parameters => T + )( + implicit p: Parameters + ) = _root_.org.chipsalliance.diplomacy.EnableMonitors(body)(p) + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + def DisableMonitors[T]( + body: Parameters => T + )( + implicit p: Parameters + ) = _root_.org.chipsalliance.diplomacy.DisableMonitors(body)(p) + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + def FlipRendering[T]( + body: Parameters => T + )( + implicit p: Parameters + ) = _root_.org.chipsalliance.diplomacy.FlipRendering(body)(p) + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + implicit def moduleValue[T](value: ModuleValue[T]): T = _root_.org.chipsalliance.diplomacy.moduleValue(value) - implicit def moduleValue[T](value: ModuleValue[T]): T = value.getWrappedValue +// Clone.scala + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val CloneLazyModule = _root_.org.chipsalliance.diplomacy.lazymodule.CloneLazyModule - implicit def noCrossing(value: NoCrossing.type): ClockCrossingType = SynchronousCrossing(BufferParams.none) +// ValName.scala + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type ValName = _root_.org.chipsalliance.diplomacy.ValName + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + def ValName(value: String) = _root_.org.chipsalliance.diplomacy.ValName(value) + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + implicit def SourcecodeNameExt(x: sourcecode.Name) = _root_.org.chipsalliance.diplomacy.SourcecodeNameExt(x) + +// LazyModule.scala + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type LazyModule = _root_.org.chipsalliance.diplomacy.lazymodule.LazyModule + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val LazyModule = _root_.org.chipsalliance.diplomacy.lazymodule.LazyModule + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type LazyModuleImpLike = _root_.org.chipsalliance.diplomacy.lazymodule.LazyModuleImpLike + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type LazyModuleImp = _root_.org.chipsalliance.diplomacy.lazymodule.LazyModuleImp + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type LazyRawModuleImp = _root_.org.chipsalliance.diplomacy.lazymodule.LazyRawModuleImp + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type SimpleLazyModule = _root_.org.chipsalliance.diplomacy.lazymodule.SimpleLazyModule + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type LazyScope = _root_.org.chipsalliance.diplomacy.lazymodule.LazyScope + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val LazyScope = _root_.org.chipsalliance.diplomacy.lazymodule.LazyScope + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type ModuleValue[T] = _root_.org.chipsalliance.diplomacy.lazymodule.ModuleValue[T] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val InModuleBody = _root_.org.chipsalliance.diplomacy.lazymodule.InModuleBody + +// Nodes.scala + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type Dangle = _root_.org.chipsalliance.diplomacy.nodes.Dangle + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val Dangle = _root_.org.chipsalliance.diplomacy.nodes.Dangle + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type HalfEdge = _root_.org.chipsalliance.diplomacy.nodes.HalfEdge + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val HalfEdge = _root_.org.chipsalliance.diplomacy.nodes.HalfEdge + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val MonitorsEnabled = _root_.org.chipsalliance.diplomacy.nodes.MonitorsEnabled + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val RenderFlipped = _root_.org.chipsalliance.diplomacy.nodes.RenderFlipped + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val RenderedEdge = _root_.org.chipsalliance.diplomacy.nodes.RenderedEdge + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type RenderedEdge = _root_.org.chipsalliance.diplomacy.nodes.RenderedEdge + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type InwardNodeImp[DI, UI, EI, BI <: Data] = _root_.org.chipsalliance.diplomacy.nodes.InwardNodeImp[DI, UI, EI, BI] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type OutwardNodeImp[DO, UO, EO, BO <: Data] = _root_.org.chipsalliance.diplomacy.nodes.OutwardNodeImp[DO, UO, EO, BO] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type NodeImp[D, U, EO, EI, B <: Data] = _root_.org.chipsalliance.diplomacy.nodes.NodeImp[D, U, EO, EI, B] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type SimpleNodeImp[D, U, E, B <: Data] = _root_.org.chipsalliance.diplomacy.nodes.SimpleNodeImp[D, U, E, B] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type BaseNode = _root_.org.chipsalliance.diplomacy.nodes.BaseNode + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val BaseNode = _root_.org.chipsalliance.diplomacy.nodes.BaseNode + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type FormatEdge = _root_.org.chipsalliance.diplomacy.nodes.FormatEdge + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type FormatNode[I <: FormatEdge, O <: FormatEdge] = _root_.org.chipsalliance.diplomacy.nodes.FormatNode[I, O] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type NoHandle = _root_.org.chipsalliance.diplomacy.nodes.NoHandle + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val NoHandleObject = _root_.org.chipsalliance.diplomacy.nodes.NoHandleObject + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type NodeHandle[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data] = + _root_.org.chipsalliance.diplomacy.nodes.NodeHandle[DI, UI, EI, BI, DO, UO, EO, BO] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val NodeHandle = _root_.org.chipsalliance.diplomacy.nodes.NodeHandle + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type NodeHandlePair[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data] = + _root_.org.chipsalliance.diplomacy.nodes.NodeHandlePair[DI, UI, EI, BI, DO, UO, EO, BO] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type InwardNodeHandle[DI, UI, EI, BI <: Data] = + _root_.org.chipsalliance.diplomacy.nodes.InwardNodeHandle[DI, UI, EI, BI] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type NodeBinding = _root_.org.chipsalliance.diplomacy.nodes.NodeBinding + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val BIND_ONCE = _root_.org.chipsalliance.diplomacy.nodes.BIND_ONCE + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val BIND_QUERY = _root_.org.chipsalliance.diplomacy.nodes.BIND_QUERY + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val BIND_STAR = _root_.org.chipsalliance.diplomacy.nodes.BIND_STAR + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val BIND_FLEX = _root_.org.chipsalliance.diplomacy.nodes.BIND_FLEX + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type InwardNode[DI, UI, BI <: Data] = _root_.org.chipsalliance.diplomacy.nodes.InwardNode[DI, UI, BI] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type OutwardNodeHandle[DO, UO, EO, BO <: Data] = + _root_.org.chipsalliance.diplomacy.nodes.OutwardNodeHandle[DO, UO, EO, BO] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type OutwardNode[DO, UO, BO <: Data] = _root_.org.chipsalliance.diplomacy.nodes.OutwardNode[DO, UO, BO] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type CycleException = _root_.org.chipsalliance.diplomacy.nodes.CycleException + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type StarCycleException = _root_.org.chipsalliance.diplomacy.nodes.StarCycleException + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type DownwardCycleException = _root_.org.chipsalliance.diplomacy.nodes.DownwardCycleException + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type UpwardCycleException = _root_.org.chipsalliance.diplomacy.nodes.UpwardCycleException + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val Edges = _root_.org.chipsalliance.diplomacy.nodes.Edges + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type Edges[EI, EO] = _root_.org.chipsalliance.diplomacy.nodes.Edges[EI, EO] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type MixedCustomNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data] = + _root_.org.chipsalliance.diplomacy.nodes.MixedCustomNode[DI, UI, EI, BI, DO, UO, EO, BO] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type CustomNode[D, U, EO, EI, B <: Data] = _root_.org.chipsalliance.diplomacy.nodes.CustomNode[D, U, EO, EI, B] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type MixedJunctionNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data] = + _root_.org.chipsalliance.diplomacy.nodes.MixedJunctionNode[DI, UI, EI, BI, DO, UO, EO, BO] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type JunctionNode[D, U, EO, EI, B <: Data] = _root_.org.chipsalliance.diplomacy.nodes.JunctionNode[D, U, EO, EI, B] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type MixedAdapterNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data] = + _root_.org.chipsalliance.diplomacy.nodes.MixedAdapterNode[DI, UI, EI, BI, DO, UO, EO, BO] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type AdapterNode[D, U, EO, EI, B <: Data] = _root_.org.chipsalliance.diplomacy.nodes.AdapterNode[D, U, EO, EI, B] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type IdentityNode[D, U, EO, EI, B <: Data] = _root_.org.chipsalliance.diplomacy.nodes.IdentityNode[D, U, EO, EI, B] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type EphemeralNode[D, U, EO, EI, B <: Data] = _root_.org.chipsalliance.diplomacy.nodes.EphemeralNode[D, U, EO, EI, B] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type MixedNexusNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data] = + _root_.org.chipsalliance.diplomacy.nodes.MixedNexusNode[DI, UI, EI, BI, DO, UO, EO, BO] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type NexusNode[D, U, EO, EI, B <: Data] = _root_.org.chipsalliance.diplomacy.nodes.NexusNode[D, U, EO, EI, B] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type SourceNode[D, U, EO, EI, B <: Data] = _root_.org.chipsalliance.diplomacy.nodes.SourceNode[D, U, EO, EI, B] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type SinkNode[D, U, EO, EI, B <: Data] = _root_.org.chipsalliance.diplomacy.nodes.SinkNode[D, U, EO, EI, B] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type SimpleNodeHandle[D, U, E, B <: Data] = _root_.org.chipsalliance.diplomacy.nodes.SimpleNodeHandle[D, U, E, B] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type AnyMixedNode = _root_.org.chipsalliance.diplomacy.nodes.AnyMixedNode - type BundleBridgeInwardNode[T <: Data] = InwardNodeHandle[BundleBridgeParams[T], BundleBridgeParams[T], BundleBridgeEdgeParams[T], T] - type BundleBridgeOutwardNode[T <: Data] = OutwardNodeHandle[BundleBridgeParams[T], BundleBridgeParams[T], BundleBridgeEdgeParams[T], T] - type BundleBridgeNode[T <: Data] = NodeHandle[BundleBridgeParams[T], BundleBridgeParams[T], BundleBridgeEdgeParams[T], T, BundleBridgeParams[T], BundleBridgeParams[T], BundleBridgeEdgeParams[T], T] +// BundleBridge.scala + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type BundleBridgeParams[T <: Data] = _root_.org.chipsalliance.diplomacy.bundlebridge.BundleBridgeParams[T] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val BundleBridgeParams = _root_.org.chipsalliance.diplomacy.bundlebridge.BundleBridgeParams + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type BundleBridgeEdgeParams[T <: Data] = _root_.org.chipsalliance.diplomacy.bundlebridge.BundleBridgeEdgeParams[T] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type BundleBridgeImp[T <: Data] = _root_.org.chipsalliance.diplomacy.bundlebridge.BundleBridgeImp[T] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type BundleBridgeSink[T <: Data] = _root_.org.chipsalliance.diplomacy.bundlebridge.BundleBridgeSink[T] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val BundleBridgeSink = _root_.org.chipsalliance.diplomacy.bundlebridge.BundleBridgeSink + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type BundleBridgeSource[T <: Data] = _root_.org.chipsalliance.diplomacy.bundlebridge.BundleBridgeSource[T] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val BundleBridgeSource = _root_.org.chipsalliance.diplomacy.bundlebridge.BundleBridgeSource + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type BundleBridgeIdentityNode[T <: Data] = _root_.org.chipsalliance.diplomacy.bundlebridge.BundleBridgeIdentityNode[T] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val BundleBridgeIdentityNode = _root_.org.chipsalliance.diplomacy.bundlebridge.BundleBridgeIdentityNode + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type BundleBridgeEphemeralNode[T <: Data] = + _root_.org.chipsalliance.diplomacy.bundlebridge.BundleBridgeEphemeralNode[T] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val BundleBridgeEphemeralNode = _root_.org.chipsalliance.diplomacy.bundlebridge.BundleBridgeEphemeralNode + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + def BundleBridgeNameNode[T <: Data](name: String) = + _root_.org.chipsalliance.diplomacy.bundlebridge.BundleBridgeNameNode[T](name) + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type BundleBridgeNexusNode[T <: Data] = _root_.org.chipsalliance.diplomacy.bundlebridge.BundleBridgeNexusNode[T] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type BundleBridgeNexus[T <: Data] = _root_.org.chipsalliance.diplomacy.bundlebridge.BundleBridgeNexus[T] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + val BundleBridgeNexus = _root_.org.chipsalliance.diplomacy.bundlebridge.BundleBridgeNexus + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + def BundleBroadcast[T <: Data]( + name: Option[String] = None, + registered: Boolean = false, + default: Option[() => T] = None, + inputRequiresOutput: Boolean = false, // when false, connecting a source does not mandate connecting a sink + shouldBeInlined: Boolean = true + )( + implicit p: Parameters + ) = _root_.org.chipsalliance.diplomacy.bundlebridge + .BundleBroadcast[T](name, registered, default, inputRequiresOutput, shouldBeInlined)(p) + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type BundleBridgeInwardNode[T <: Data] = _root_.org.chipsalliance.diplomacy.bundlebridge.BundleBridgeInwardNode[T] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type BundleBridgeOutwardNode[T <: Data] = _root_.org.chipsalliance.diplomacy.bundlebridge.BundleBridgeOutwardNode[T] + @deprecated("Diplomacy has been split to a standalone library", "rocketchip 2.0.0") + type BundleBridgeNode[T <: Data] = _root_.org.chipsalliance.diplomacy.bundlebridge.BundleBridgeNode[T] } diff --git a/src/main/scala/util/HeterogeneousBag.scala b/src/main/scala/util/HeterogeneousBag.scala deleted file mode 100644 index f9a591b4b4..0000000000 --- a/src/main/scala/util/HeterogeneousBag.scala +++ /dev/null @@ -1,24 +0,0 @@ -// See LICENSE.SiFive for license details. - -package freechips.rocketchip.util - -import chisel3._ -import chisel3.Record -import chisel3.reflect.DataMirror.internal.chiselTypeClone -import scala.collection.immutable.ListMap - -final case class HeterogeneousBag[T <: Data](elts: Seq[T]) extends Record with collection.IndexedSeq[T] { - def apply(x: Int) = elements(x.toString).asInstanceOf[T] - def length = elts.length - - override def className: String = super.className - val elements = ListMap(elts.zipWithIndex.map { case (n,i) => (i.toString, chiselTypeClone(n)) }:_*) - // IndexedSeq has its own hashCode/equals that we must not use - override def hashCode: Int = super[Record].hashCode - override def equals(that: Any): Boolean = super[Record].equals(that) -} - -object HeterogeneousBag -{ - def fromNode[D <: Data, E](elts: Seq[(D, E)]) = new HeterogeneousBag(elts.map(_._1.cloneType)) -} diff --git a/src/main/scala/util/package.scala b/src/main/scala/util/package.scala index bacaf5b7b1..43eddcb236 100644 --- a/src/main/scala/util/package.scala +++ b/src/main/scala/util/package.scala @@ -286,4 +286,10 @@ package object util { case x if x == n => in case _ => throw new Exception(s"must provide exactly 1 or $n of some field, but got:\n$in") } + + // HeterogeneousBag moved to standalond diplomacy + @deprecated("HeterogeneousBag has been absorbed into standalone diplomacy library", "rocketchip 2.0.0") + def HeterogeneousBag[T <: Data](elts: Seq[T]) = _root_.org.chipsalliance.diplomacy.nodes.HeterogeneousBag[T](elts) + @deprecated("HeterogeneousBag has been absorbed into standalone diplomacy library", "rocketchip 2.0.0") + val HeterogeneousBag = _root_.org.chipsalliance.diplomacy.nodes.HeterogeneousBag }