From f2e438833c3023b31d30bc32616de2e9e5e5790f Mon Sep 17 00:00:00 2001 From: "Wesley W. Terpstra" Date: Thu, 29 Sep 2016 14:30:19 -0700 Subject: [PATCH] tilelink2 Nodes: generalize a node into inner and outer halves This lets us create nodes which transform from one bus to another. --- .../scala/uncore/tilelink2/IntNodes.scala | 19 +- src/main/scala/uncore/tilelink2/Nodes.scala | 162 ++++++++++++------ .../scala/uncore/tilelink2/Parameters.scala | 12 +- src/main/scala/uncore/tilelink2/TLNodes.scala | 17 +- 4 files changed, 128 insertions(+), 82 deletions(-) diff --git a/src/main/scala/uncore/tilelink2/IntNodes.scala b/src/main/scala/uncore/tilelink2/IntNodes.scala index 5ddfcd5b..acf1cdc8 100644 --- a/src/main/scala/uncore/tilelink2/IntNodes.scala +++ b/src/main/scala/uncore/tilelink2/IntNodes.scala @@ -23,13 +23,13 @@ object IntRange case class IntSourceParameters( range: IntRange, - nodePath: Seq[IntBaseNode] = Seq()) + nodePath: Seq[RootNode] = Seq()) { val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") } case class IntSinkParameters( - nodePath: Seq[IntBaseNode] = Seq()) + nodePath: Seq[RootNode] = Seq()) { val name = nodePath.lastOption.map(_.lazyModule.name).getOrElse("disconnected") } @@ -49,8 +49,8 @@ case class IntEdge(source: IntSourcePortParameters, sink: IntSinkPortParameters) object IntImp extends NodeImp[IntSourcePortParameters, IntSinkPortParameters, IntEdge, IntEdge, Vec[Bool]] { - def edgeO(po: IntSourcePortParameters, pi: IntSinkPortParameters): IntEdge = IntEdge(po, pi) - def edgeI(po: IntSourcePortParameters, pi: IntSinkPortParameters): IntEdge = IntEdge(po, pi) + def edgeO(pd: IntSourcePortParameters, pu: IntSinkPortParameters): IntEdge = IntEdge(pd, pu) + def edgeI(pd: IntSourcePortParameters, pu: IntSinkPortParameters): IntEdge = IntEdge(pd, pu) def bundleO(eo: Seq[IntEdge]): Vec[Vec[Bool]] = { if (eo.isEmpty) Vec(0, Vec(0, Bool())) else Vec(eo.size, Vec(eo.map(_.source.num).max, Bool())) @@ -60,18 +60,17 @@ object IntImp extends NodeImp[IntSourcePortParameters, IntSinkPortParameters, In Vec(ei.size, Vec(ei.map(_.source.num).max, Bool())).flip } - def connect(bo: => Vec[Bool], eo: => IntEdge, bi: => Vec[Bool], ei: => IntEdge)(implicit sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = { + def connect(bo: => Vec[Bool], bi: => Vec[Bool], ei: => IntEdge)(implicit sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = { (None, () => { - require (eo == ei) // Cannot use bulk connect, because the widths could differ (bo zip bi) foreach { case (o, i) => i := o } }) } - override def mixO(po: IntSourcePortParameters, node: IntBaseNode): IntSourcePortParameters = - po.copy(sources = po.sources.map { s => s.copy (nodePath = node +: s.nodePath) }) - override def mixI(pi: IntSinkPortParameters, node: IntBaseNode): IntSinkPortParameters = - pi.copy(sinks = pi.sinks.map { s => s.copy (nodePath = node +: s.nodePath) }) + override def mixO(pd: IntSourcePortParameters, node: OutwardNode[IntSourcePortParameters, IntSinkPortParameters, Vec[Bool]]): IntSourcePortParameters = + pd.copy(sources = pd.sources.map { s => s.copy (nodePath = node +: s.nodePath) }) + override def mixI(pu: IntSinkPortParameters, node: InwardNode[IntSourcePortParameters, IntSinkPortParameters, Vec[Bool]]): IntSinkPortParameters = + pu.copy(sinks = pu.sinks.map { s => s.copy (nodePath = node +: s.nodePath) }) } case class IntIdentityNode() extends IdentityNode(IntImp) diff --git a/src/main/scala/uncore/tilelink2/Nodes.scala b/src/main/scala/uncore/tilelink2/Nodes.scala index 99e13158..93c89e13 100644 --- a/src/main/scala/uncore/tilelink2/Nodes.scala +++ b/src/main/scala/uncore/tilelink2/Nodes.scala @@ -6,22 +6,32 @@ import Chisel._ import scala.collection.mutable.ListBuffer import chisel3.internal.sourceinfo.SourceInfo -// PI = PortInputParameters -// PO = PortOutputParameters -// EI = EdgeInput -// EO = EdgeOutput -abstract class NodeImp[PO, PI, EO, EI, B <: Data] +// DI = Downwards flowing Parameters received on the inner side of the node +// UI = Upwards flowing Parameters generated by the inner side of the node +// EI = Edge Parameters describing a connection on the inner side of the node +// BI = Bundle type used when connecting to the inner side of the node +trait InwardNodeImp[DI, UI, EI, BI <: Data] { - def edgeO(po: PO, pi: PI): EO - def edgeI(po: PO, pi: PI): EI - def bundleO(eo: Seq[EO]): Vec[B] - def bundleI(ei: Seq[EI]): Vec[B] - def connect(bo: => B, eo: => EO, bi: => B, ei: => EI)(implicit sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) - // If you want to track parameters as they flow through nodes, overload these: - def mixO(po: PO, node: BaseNode[PO, PI, EO, EI, B]): PO = po - def mixI(pi: PI, node: BaseNode[PO, PI, EO, EI, B]): PI = pi + def edgeI(pd: DI, pu: UI): EI + def bundleI(ei: Seq[EI]): Vec[BI] + def mixI(pu: UI, node: InwardNode[DI, UI, BI]): UI = pu + def connect(bo: => BI, bi: => BI, e: => EI)(implicit sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) } +// DO = Downwards flowing Parameters generated by the outner side of the node +// UO = Upwards flowing Parameters received on the outner side of the node +// EO = Edge Parameters describing a connection on the outer side of the node +// BO = Bundle type used when connecting to the outer side of the node +trait OutwardNodeImp[DO, UO, EO, BO <: Data] +{ + def edgeO(pd: DO, pu: UO): EO + def bundleO(eo: Seq[EO]): Vec[BO] + def mixO(pd: DO, node: OutwardNode[DO, UO, BO]): DO = pd +} + +abstract class NodeImp[D, U, EO, EI, B <: Data] + extends Object with InwardNodeImp[D, U, EI, B] with OutwardNodeImp[D, U, EO, B] + abstract class RootNode { // You cannot create a Node outside a LazyModule! @@ -39,87 +49,125 @@ abstract class RootNode protected[tilelink2] def inputs: Seq[RootNode] } -class BaseNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])( - private val oFn: (Int, Seq[PO]) => Seq[PO], - private val iFn: (Int, Seq[PI]) => Seq[PI], - private val numPO: Range.Inclusive, - private val numPI: Range.Inclusive) extends RootNode +trait InwardNode[DI, UI, BI <: Data] extends RootNode { - // At least 0 ports must be supported - require (!numPO.isEmpty, s"No number of outputs would be acceptable to ${name}${lazyModule.line}") + protected[tilelink2] val numPI: Range.Inclusive require (!numPI.isEmpty, s"No number of inputs would be acceptable to ${name}${lazyModule.line}") - require (numPO.start >= 0, s"${name} accepts a negative number of outputs${lazyModule.line}") require (numPI.start >= 0, s"${name} accepts a negative number of inputs${lazyModule.line}") - val noOs = numPO.size == 1 && numPO.contains(0) - val noIs = numPI.size == 1 && numPI.contains(0) - - private val accPO = ListBuffer[(Int, BaseNode[PO, PI, EO, EI, B])]() - private val accPI = ListBuffer[(Int, BaseNode[PO, PI, EO, EI, B])]() - private var oRealized = false + private val accPI = ListBuffer[(Int, OutwardNode[DI, UI, BI])]() private var iRealized = false - private def reqO() = require(numPO.contains(accPO.size), s"${name} has ${accPO.size} outputs, expected ${numPO}${lazyModule.line}") + protected[tilelink2] def iPushed = accPI.size + protected[tilelink2] def iPush(index: Int, node: OutwardNode[DI, UI, BI])(implicit sourceInfo: SourceInfo) { + val info = sourceLine(sourceInfo, " at ", "") + val noIs = numPI.size == 1 && numPI.contains(0) + require (!noIs, s"${name}${lazyModule.line} was incorrectly connected as a sink" + info) + require (!iRealized, s"${name}${lazyModule.line} was incorrectly connected as a sink after it's .module was used" + info) + accPI += ((index, node)) + } + private def reqI() = require(numPI.contains(accPI.size), s"${name} has ${accPI.size} inputs, expected ${numPI}${lazyModule.line}") - protected def reqE(o: Int, i: Int) = require(i == o, s"${name} has ${i} inputs and ${o} outputs; they must match${lazyModule.line}") + protected[tilelink2] lazy val iPorts = { iRealized = true; reqI(); accPI.result() } - private lazy val oPorts = { oRealized = true; reqO(); accPO.result() } - private lazy val iPorts = { iRealized = true; reqI(); accPI.result() } + protected[tilelink2] val iParams: Seq[UI] + protected[tilelink2] def iConnect: Vec[BI] +} +trait OutwardNode[DO, UO, BO <: Data] extends RootNode +{ + protected[tilelink2] val numPO: Range.Inclusive + require (!numPO.isEmpty, s"No number of outputs would be acceptable to ${name}${lazyModule.line}") + require (numPO.start >= 0, s"${name} accepts a negative number of outputs${lazyModule.line}") + + private val accPO = ListBuffer[(Int, InwardNode [DO, UO, BO])]() + private var oRealized = false + + protected[tilelink2] def oPushed = accPO.size + protected[tilelink2] def oPush(index: Int, node: InwardNode [DO, UO, BO])(implicit sourceInfo: SourceInfo) { + val info = sourceLine(sourceInfo, " at ", "") + val noOs = numPO.size == 1 && numPO.contains(0) + require (!noOs, s"${name}${lazyModule.line} was incorrectly connected as a source" + info) + require (!oRealized, s"${name}${lazyModule.line} was incorrectly connected as a source after it's .module was used" + info) + accPO += ((index, node)) + } + + private def reqO() = require(numPO.contains(accPO.size), s"${name} has ${accPO.size} outputs, expected ${numPO}${lazyModule.line}") + protected[tilelink2] lazy val oPorts = { oRealized = true; reqO(); accPO.result() } + + protected[tilelink2] val oParams: Seq[DO] + protected[tilelink2] def oConnect: Vec[BO] +} + +class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( + inner: InwardNodeImp [DI, UI, EI, BI], + outer: OutwardNodeImp[DO, UO, EO, BO])( + private val dFn: (Int, Seq[DI]) => Seq[DO], + private val uFn: (Int, Seq[UO]) => Seq[UI], + protected[tilelink2] val numPO: Range.Inclusive, + protected[tilelink2] val numPI: Range.Inclusive) + extends RootNode with InwardNode[DI, UI, BI] with OutwardNode[DO, UO, BO] +{ + // meta-data for printing the node graph protected[tilelink2] def outputs = oPorts.map(_._2) protected[tilelink2] def inputs = iPorts.map(_._2) - private lazy val oParams : Seq[PO] = { - val o = oFn(oPorts.size, iPorts.map{ case (i, n) => n.oParams(i) }) + private def reqE(o: Int, i: Int) = require(i == o, s"${name} has ${i} inputs and ${o} outputs; they must match${lazyModule.line}") + protected[tilelink2] lazy val oParams: Seq[DO] = { + val o = dFn(oPorts.size, iPorts.map { case (i, n) => n.oParams(i) }) reqE(oPorts.size, o.size) - o.map(imp.mixO(_, this)) + o.map(outer.mixO(_, this)) } - private lazy val iParams : Seq[PI] = { - val i = iFn(iPorts.size, oPorts.map{ case (o, n) => n.iParams(o) }) + protected[tilelink2] lazy val iParams: Seq[UI] = { + val i = uFn(iPorts.size, oPorts.map { case (o, n) => n.iParams(o) }) reqE(i.size, iPorts.size) - i.map(imp.mixI(_, this)) + i.map(inner.mixI(_, this)) } - lazy val edgesOut = (oPorts zip oParams).map { case ((i, n), o) => imp.edgeO(o, n.iParams(i)) } - lazy val edgesIn = (iPorts zip iParams).map { case ((o, n), i) => imp.edgeI(n.oParams(o), i) } + lazy val edgesOut = (oPorts zip oParams).map { case ((i, n), o) => outer.edgeO(o, n.iParams(i)) } + lazy val edgesIn = (iPorts zip iParams).map { case ((o, n), i) => inner.edgeI(n.oParams(o), i) } - lazy val bundleOut = imp.bundleO(edgesOut) - lazy val bundleIn = imp.bundleI(edgesIn) + lazy val bundleOut = outer.bundleO(edgesOut) + lazy val bundleIn = inner.bundleI(edgesIn) - def connectOut = bundleOut - def connectIn = bundleIn + def oConnect = bundleOut + def iConnect = bundleIn - def := (y: BaseNode[PO, PI, EO, EI, B])(implicit sourceInfo: SourceInfo): Option[LazyModule] = { + // connects the outward part of a node with the inward part of this node + def := (y: OutwardNode[DI, UI, BI])(implicit sourceInfo: SourceInfo): Option[LazyModule] = { val x = this // x := y val info = sourceLine(sourceInfo, " at ", "") require (!LazyModule.stack.isEmpty, s"${y.name} cannot be connected to ${x.name} outside of LazyModule scope" + info) - require (!y.noOs, s"${y.name}${y.lazyModule.line} was incorrectly connected as a source" + info) - require (!y.oRealized, s"${y.name}${y.lazyModule.line} was incorrectly connected as a source after it's .module was used" + info) - require (!x.noIs, s"${x.name}${x.lazyModule.line} was incorrectly connected as a sink" + info) - require (!x.iRealized, s"${x.name}${x.lazyModule.line} was incorrectly connected as a sink after it's .module was used" + info) - val i = x.accPI.size - val o = y.accPO.size - y.accPO += ((i, x)) - x.accPI += ((o, y)) - val (out, binding) = imp.connect(y.connectOut(o), y.edgesOut(o), x.connectIn(i), x.edgesIn(i)) + val i = x.iPushed + val o = y.oPushed + y.oPush(i, x) + x.iPush(o, y) + val (out, binding) = inner.connect(y.oConnect(o), x.iConnect(i), x.edgesIn(i)) LazyModule.stack.head.bindings = binding :: LazyModule.stack.head.bindings out } } +class BaseNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])( + oFn: (Int, Seq[D]) => Seq[D], + iFn: (Int, Seq[U]) => Seq[U], + numPO: Range.Inclusive, + numPI: Range.Inclusive) + extends MixedNode[D, U, EI, B, D, U, EO, B](imp, imp)(oFn, iFn, numPO, numPI) + class IdentityNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) extends BaseNode(imp)({case (_, s) => s}, {case (_, s) => s}, 0 to 999, 0 to 999) class OutputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) extends IdentityNode(imp) { - override def connectOut = bundleOut - override def connectIn = bundleOut + override def oConnect = bundleOut + override def iConnect = bundleOut } class InputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) extends IdentityNode(imp) { - override def connectOut = bundleIn - override def connectIn = bundleIn + override def oConnect = bundleIn + override def iConnect = bundleIn } class SourceNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(po: PO, num: Range.Inclusive = 1 to 1) diff --git a/src/main/scala/uncore/tilelink2/Parameters.scala b/src/main/scala/uncore/tilelink2/Parameters.scala index d226ef89..463ac2e3 100644 --- a/src/main/scala/uncore/tilelink2/Parameters.scala +++ b/src/main/scala/uncore/tilelink2/Parameters.scala @@ -146,10 +146,10 @@ object AddressSet case class TLManagerParameters( address: Seq[AddressSet], - sinkId: IdRange = IdRange(0, 1), - regionType: RegionType.T = RegionType.GET_EFFECTS, - executable: Boolean = false, // processor can execute from this memory - nodePath: Seq[TLBaseNode] = Seq(), + sinkId: IdRange = IdRange(0, 1), + regionType: RegionType.T = RegionType.GET_EFFECTS, + executable: Boolean = false, // processor can execute from this memory + nodePath: Seq[RootNode] = Seq(), // Supports both Acquire+Release+Finish of these sizes supportsAcquire: TransferSizes = TransferSizes.none, supportsArithmetic: TransferSizes = TransferSizes.none, @@ -292,8 +292,8 @@ case class TLManagerPortParameters( } case class TLClientParameters( - sourceId: IdRange = IdRange(0,1), - nodePath: Seq[TLBaseNode] = Seq(), + sourceId: IdRange = IdRange(0,1), + nodePath: Seq[RootNode] = Seq(), // Supports both Probe+Grant of these sizes supportsProbe: TransferSizes = TransferSizes.none, supportsArithmetic: TransferSizes = TransferSizes.none, diff --git a/src/main/scala/uncore/tilelink2/TLNodes.scala b/src/main/scala/uncore/tilelink2/TLNodes.scala index 2ab3738d..84c1405e 100644 --- a/src/main/scala/uncore/tilelink2/TLNodes.scala +++ b/src/main/scala/uncore/tilelink2/TLNodes.scala @@ -8,8 +8,8 @@ import chisel3.internal.sourceinfo.SourceInfo object TLImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle] { - def edgeO(po: TLClientPortParameters, pi: TLManagerPortParameters): TLEdgeOut = new TLEdgeOut(po, pi) - def edgeI(po: TLClientPortParameters, pi: TLManagerPortParameters): TLEdgeIn = new TLEdgeIn(po, pi) + def edgeO(pd: TLClientPortParameters, pu: TLManagerPortParameters): TLEdgeOut = new TLEdgeOut(pd, pu) + def edgeI(pd: TLClientPortParameters, pu: TLManagerPortParameters): TLEdgeIn = new TLEdgeIn(pd, pu) def bundleO(eo: Seq[TLEdgeOut]): Vec[TLBundle] = { require (!eo.isEmpty) Vec(eo.size, TLBundle(eo.map(_.bundle).reduce(_.union(_)))) @@ -19,19 +19,18 @@ object TLImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TL Vec(ei.size, TLBundle(ei.map(_.bundle).reduce(_.union(_)))).flip } - def connect(bo: => TLBundle, eo: => TLEdgeOut, bi: => TLBundle, ei: => TLEdgeIn)(implicit sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = { - val monitor = LazyModule(new TLMonitor(() => new TLBundleSnoop(bo.params), () => eo, sourceInfo)) + def connect(bo: => TLBundle, bi: => TLBundle, ei: => TLEdgeIn)(implicit sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = { + val monitor = LazyModule(new TLMonitor(() => new TLBundleSnoop(bo.params), () => ei, sourceInfo)) (Some(monitor), () => { - require (eo.asInstanceOf[TLEdgeParameters] == ei.asInstanceOf[TLEdgeParameters]) bi <> bo monitor.module.io.in := TLBundleSnoop(bo) }) } - override def mixO(po: TLClientPortParameters, node: TLBaseNode): TLClientPortParameters = - po.copy(clients = po.clients.map { c => c.copy (nodePath = node +: c.nodePath) }) - override def mixI(pi: TLManagerPortParameters, node: TLBaseNode): TLManagerPortParameters = - pi.copy(managers = pi.managers.map { m => m.copy (nodePath = node +: m.nodePath) }) + override def mixO(pd: TLClientPortParameters, node: OutwardNode[TLClientPortParameters, TLManagerPortParameters, TLBundle]): TLClientPortParameters = + pd.copy(clients = pd.clients.map { c => c.copy (nodePath = node +: c.nodePath) }) + override def mixI(pu: TLManagerPortParameters, node: InwardNode[TLClientPortParameters, TLManagerPortParameters, TLBundle]): TLManagerPortParameters = + pu.copy(managers = pu.managers.map { m => m.copy (nodePath = node +: m.nodePath) }) } case class TLIdentityNode() extends IdentityNode(TLImp)