diff --git a/src/main/scala/coreplex/CoreplexNetwork.scala b/src/main/scala/coreplex/CoreplexNetwork.scala index 4b88a09d..7d64ec8c 100644 --- a/src/main/scala/coreplex/CoreplexNetwork.scala +++ b/src/main/scala/coreplex/CoreplexNetwork.scala @@ -13,6 +13,8 @@ trait CoreplexNetwork extends HasCoreplexParameters { val module: CoreplexNetworkModule def bindingTree: ResourceMap + val tile_splitter = LazyModule(new TLSplitter) + val l1tol2 = LazyModule(new TLXbar) val l1tol2_beatBytes = l1tol2Config.beatBytes val l1tol2_lineBytes = p(CacheBlockBytes) @@ -34,6 +36,7 @@ trait CoreplexNetwork extends HasCoreplexParameters { private val l2in_buffer = LazyModule(new TLBuffer) private val l2in_fifo = LazyModule(new TLFIFOFixer) l1tol2.node :=* l2in_fifo.node + l1tol2.node :=* tile_splitter.node l2in_fifo.node :=* l2in_buffer.node l2in_buffer.node :=* l2in @@ -83,7 +86,7 @@ trait CoreplexNetwork extends HasCoreplexParameters { } // Make topManagers an Option[] so as to avoid LM name reflection evaluating it... - lazy val topManagers = Some(ManagerUnification(l1tol2.node.edgesIn.headOption.map(_.manager.managers).getOrElse(Nil))) + lazy val topManagers = Some(ManagerUnification(tile_splitter.node.edgesIn.headOption.map(_.manager.managers).getOrElse(Nil))) ResourceBinding { val managers = topManagers.get val max = managers.flatMap(_.address).map(_.max).max diff --git a/src/main/scala/coreplex/ISPPort.scala b/src/main/scala/coreplex/ISPPort.scala new file mode 100644 index 00000000..4296cd20 --- /dev/null +++ b/src/main/scala/coreplex/ISPPort.scala @@ -0,0 +1,44 @@ +// See LICENSE.SiFive for license details. + +package coreplex + +import Chisel._ +import config._ +import diplomacy._ +import rocket._ +import tile._ +import uncore.tilelink2._ +import util._ + +trait HasISPPort extends CoreplexNetwork { + val module: HasISPPortModule + + // TODO: use ChipLink instead of AsyncTileLink + val isp_in = TLAsyncInputNode() + val isp_out = TLAsyncOutputNode() + + private val out_xbar = LazyModule(new TLXbar) + private val out_nums = LazyModule(new TLNodeNumberer) + private val out_async = LazyModule(new TLAsyncCrossingSource) + out_xbar.node :=* tile_splitter.node + out_nums.node :*= out_xbar.node + out_async.node :*= out_nums.node + isp_out :*= out_async.node + + private val in_async = LazyModule(new TLAsyncCrossingSink) + in_async.node :=* isp_in + l1tol2.node :=* in_async.node +} + +trait HasISPPortBundle extends CoreplexNetworkBundle { + val outer: HasISPPort + + // TODO: move to IO(...) in Module? + val isp_in = outer.isp_in.bundleIn + val isp_out = outer.isp_out.bundleOut +} + +trait HasISPPortModule extends CoreplexNetworkModule { + val outer: HasISPPort + val io: HasISPPortBundle +} diff --git a/src/main/scala/coreplex/RocketTiles.scala b/src/main/scala/coreplex/RocketTiles.scala index 3b7acd67..2e8e9562 100644 --- a/src/main/scala/coreplex/RocketTiles.scala +++ b/src/main/scala/coreplex/RocketTiles.scala @@ -36,7 +36,7 @@ trait HasRocketTiles extends CoreplexRISCVPlatform { val pWithExtra = p.alterPartial { case TileKey => c case BuildRoCC => c.rocc - case SharedMemoryTLEdge => l1tol2.node.edgesIn(0) + case SharedMemoryTLEdge => tile_splitter.node.edgesIn(0) } val asyncIntXbar = LazyModule(new IntXbar) @@ -64,7 +64,7 @@ trait HasRocketTiles extends CoreplexRISCVPlatform { val fixer = LazyModule(new TLFIFOFixer) buffer.node :=* wrapper.masterNode fixer.node :=* buffer.node - l1tol2.node :=* fixer.node + tile_splitter.node :=* fixer.node wrapper.slaveNode :*= cbus.node wrapper.asyncIntNode := asyncIntXbar.intnode wrapper.periphIntNode := periphIntXbar.intnode @@ -82,7 +82,7 @@ trait HasRocketTiles extends CoreplexRISCVPlatform { val fixer = LazyModule(new TLFIFOFixer) sink.node :=* wrapper.masterNode fixer.node :=* sink.node - l1tol2.node :=* fixer.node + tile_splitter.node :=* fixer.node wrapper.slaveNode :*= source.node wrapper.asyncIntNode := asyncIntXbar.intnode wrapper.periphIntNode := periphIntXbar.intnode @@ -102,7 +102,7 @@ trait HasRocketTiles extends CoreplexRISCVPlatform { val fixer = LazyModule(new TLFIFOFixer) sink.node :=* wrapper.masterNode fixer.node :=* sink.node - l1tol2.node :=* fixer.node + tile_splitter.node :=* fixer.node wrapper.slaveNode :*= source.node wrapper.asyncIntNode := asyncIntXbar.intnode wrapper.periphIntNode := periphIntXbar.intnode diff --git a/src/main/scala/diplomacy/Nodes.scala b/src/main/scala/diplomacy/Nodes.scala index 11aedb97..92f00e1e 100644 --- a/src/main/scala/diplomacy/Nodes.scala +++ b/src/main/scala/diplomacy/Nodes.scala @@ -159,15 +159,13 @@ abstract class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( protected[diplomacy] val numPI: Range.Inclusive) extends BaseNode with InwardNode[DI, UI, BI] with OutwardNode[DO, UO, BO] { - protected[diplomacy] def resolveStarO(i: Int, o: Int): Int - protected[diplomacy] def resolveStarI(i: Int, o: Int): Int + protected[diplomacy] def resolveStar(iKnown: Int, oKnown: Int, iStar: Int, oStar: Int): (Int, Int) protected[diplomacy] def mapParamsD(n: Int, p: Seq[DI]): Seq[DO] protected[diplomacy] def mapParamsU(n: Int, p: Seq[UO]): Seq[UI] protected[diplomacy] lazy val (oPortMapping, iPortMapping, oStar, iStar) = { val oStars = oBindings.filter { case (_,_,b) => b == BIND_STAR }.size val iStars = iBindings.filter { case (_,_,b) => b == BIND_STAR }.size - require (oStars + iStars <= 1, s"${name} appears beside a :*= ${iStars} times and a :=* ${oStars} times; at most once is allowed${lazyModule.line}") val oKnown = oBindings.map { case (_, n, b) => b match { case BIND_ONCE => 1 case BIND_QUERY => n.iStar @@ -176,8 +174,7 @@ abstract class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( case BIND_ONCE => 1 case BIND_QUERY => n.oStar case BIND_STAR => 0 }}.foldLeft(0)(_+_) - val oStar = if (oStars > 0) resolveStarO(iKnown, oKnown) else 0 - val iStar = if (iStars > 0) resolveStarI(iKnown, oKnown) else 0 + val (iStar, oStar) = resolveStar(iKnown, oKnown, iStars, oStars) val oSum = oBindings.map { case (_, n, b) => b match { case BIND_ONCE => 1 case BIND_QUERY => n.iStar @@ -274,6 +271,23 @@ abstract class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( protected[diplomacy] def inputs = iPorts.map(_._2) zip edgesIn .map(e => inner.labelI(e)) } +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])( + numPO: Range.Inclusive, + numPI: Range.Inclusive) + extends MixedNode(inner, outer)(numPO, numPI) +{ + 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] +} + +abstract class CustomNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])( + numPO: Range.Inclusive, + numPI: Range.Inclusive) + extends MixedCustomNode(imp, imp)(numPO, numPI) + class MixedAdapterNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( inner: InwardNodeImp [DI, UI, EI, BI], outer: OutwardNodeImp[DO, UO, EO, BO])( @@ -285,13 +299,15 @@ class MixedAdapterNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( val externalIn: Boolean = true val externalOut: Boolean = true - protected[diplomacy] def resolveStarO(i: Int, o: Int): Int = { - require (i >= o, s"${name} has ${o} outputs and ${i} inputs; cannot assign ${i-o} edges to resolve :=*${lazyModule.line}") - i - o - } - protected[diplomacy] def resolveStarI(i: Int, o: Int): Int = { - require (o >= i, s"${name} has ${o} outputs and ${i} inputs; cannot assign ${o-i} edges to resolve :*=${lazyModule.line}") - o - i + protected[diplomacy] def resolveStar(iKnown: Int, oKnown: Int, iStars: Int, oStars: Int): (Int, Int) = { + require (oStars + iStars <= 1, s"${name} (an adapter) appears left of a :*= ${iStars} times and right of a :=* ${oStars} times; at most once is allowed${lazyModule.line}") + if (oStars > 0) { + require (iKnown >= oKnown, s"${name} (an adapter) has ${oKnown} outputs and ${iKnown} inputs; cannot assign ${iKnown-oKnown} edges to resolve :=*${lazyModule.line}") + (0, iKnown - oKnown) + } else { + require (oKnown >= iKnown, s"${name} (an adapter) has ${oKnown} outputs and ${iKnown} inputs; cannot assign ${oKnown-iKnown} edges to resolve :*=${lazyModule.line}") + (oKnown - iKnown, 0) + } } protected[diplomacy] def mapParamsD(n: Int, p: Seq[DI]): Seq[DO] = { require(n == p.size, s"${name} has ${p.size} inputs and ${n} outputs; they must match${lazyModule.line}") @@ -318,13 +334,10 @@ class MixedNexusNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( val externalIn: Boolean = true val externalOut: Boolean = true - protected[diplomacy] def resolveStarO(i: Int, o: Int): Int = { - require (false, "${name} cannot resolve :=*${lazyModule.line}") - 0 - } - protected[diplomacy] def resolveStarI(i: Int, o: Int): Int = { - require (false, s"${name} cannot resolve :*=${lazyModule.line}") - 0 + protected[diplomacy] def resolveStar(iKnown: Int, oKnown: Int, iStars: Int, oStars: Int): (Int, Int) = { + require (iStars == 0, s"${name} (a nexus) appears left of :*= (perhaps you should flip the '*' to :=*?)${lazyModule.line}") + require (oStars == 0, s"${name} (a nexus) appears right of a :=* (perhaps you should flip the '*' to :*=?)${lazyModule.line}") + (0, 0) } protected[diplomacy] def mapParamsD(n: Int, p: Seq[DI]): Seq[DO] = Seq.fill(n) { dFn(p) } protected[diplomacy] def mapParamsU(n: Int, p: Seq[UO]): Seq[UI] = Seq.fill(n) { uFn(p) } @@ -343,6 +356,45 @@ class NexusNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])( numPI: Range.Inclusive = 1 to 999) extends MixedNexusNode[D, U, EI, B, D, U, EO, B](imp, imp)(dFn, uFn, numPO, numPI) +case class SplitterArg[T](newSize: Int, ports: Seq[T]) +class MixedSplitterNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data]( + inner: InwardNodeImp [DI, UI, EI, BI], + outer: OutwardNodeImp[DO, UO, EO, BO])( + dFn: SplitterArg[DI] => Seq[DO], + uFn: SplitterArg[UO] => Seq[UI], + numPO: Range.Inclusive = 1 to 999, + numPI: Range.Inclusive = 1 to 999) + extends MixedNode(inner, outer)(numPO, numPI) +{ + override val externalIn: Boolean = true + override val externalOut: Boolean = true + + protected[diplomacy] def resolveStar(iKnown: Int, oKnown: Int, iStars: Int, oStars: Int): (Int, Int) = { + require (oKnown == 0, s"${name} (a splitter) appears right of a := or :*=; use a :=* instead${lazyModule.line}") + require (iStars == 0, s"${name} (a splitter) cannot appear left of a :*=; did you mean :=*?${lazyModule.line}") + (0, iKnown) + } + protected[diplomacy] def mapParamsD(n: Int, p: Seq[DI]): Seq[DO] = { + require (p.size == 0 || n % p.size == 0, s"Diplomacy bug; splitter inputs do not divide outputs") + val out = dFn(SplitterArg(n, p)) + require (out.size == n, s"${name} created the wrong number of outputs from inputs${lazyModule.line}") + out + } + protected[diplomacy] def mapParamsU(n: Int, p: Seq[UO]): Seq[UI] = { + require (n == 0 || p.size % n == 0, s"Diplomacy bug; splitter outputs indivisable by inputs") + val out = uFn(SplitterArg(n, p)) + require (out.size == n, s"${name} created the wrong number of inputs from outputs${lazyModule.line}") + out + } +} + +class SplitterNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])( + dFn: SplitterArg[D] => Seq[D], + uFn: SplitterArg[U] => Seq[U], + numPO: Range.Inclusive = 1 to 999, + numPI: Range.Inclusive = 1 to 999) + extends MixedSplitterNode[D, U, EI, B, D, U, EO, B](imp, imp)(dFn, uFn, numPO, numPI) + class IdentityNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B]) extends AdapterNode(imp)({s => s}, {s => s}) @@ -367,13 +419,12 @@ class SourceNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(po: Seq override val externalIn: Boolean = false override val externalOut: Boolean = true - protected[diplomacy] def resolveStarO(i: Int, o: Int): Int = { - require (po.size >= o, s"${name} has ${o} outputs out of ${po.size}; cannot assign ${po.size - o} edges to resolve :=*${lazyModule.line}") - po.size - o - } - protected[diplomacy] def resolveStarI(i: Int, o: Int): Int = { - require (false, s"${name} cannot resolve :*=${lazyModule.line}") - 0 + protected[diplomacy] def resolveStar(iKnown: Int, oKnown: Int, iStars: Int, oStars: Int): (Int, Int) = { + require (oStars <= 1, s"${name} (a source) appears right of a :=* ${oStars} times; at most once is allowed${lazyModule.line}") + require (iStars == 0, s"${name} (a source) cannot appear left of a :*=${lazyModule.line}") + require (iKnown == 0, s"${name} (a source) cannot appear left of a :=${lazyModule.line}") + require (po.size >= oKnown, s"${name} (a source) has ${oKnown} outputs out of ${po.size}; cannot assign ${po.size - oKnown} edges to resolve :=*${lazyModule.line}") + (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() @@ -387,13 +438,12 @@ class SinkNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(pi: Seq[U override val externalIn: Boolean = true override val externalOut: Boolean = false - protected[diplomacy] def resolveStarO(i: Int, o: Int): Int = { - require (false, s"${name} cannot resolve :=*${lazyModule.line}") - 0 - } - protected[diplomacy] def resolveStarI(i: Int, o: Int): Int = { - require (pi.size >= i, s"${name} has ${i} inputs out of ${pi.size}; cannot assign ${pi.size - i} edges to resolve :*=${lazyModule.line}") - pi.size - i + protected[diplomacy] def resolveStar(iKnown: Int, oKnown: Int, iStars: Int, oStars: Int): (Int, Int) = { + require (iStars <= 1, s"${name} (a sink) appears left of a :*= ${iStars} times; at most once is allowed${lazyModule.line}") + require (oStars == 0, s"${name} (a sink) cannot appear right of a :=*${lazyModule.line}") + require (oKnown == 0, s"${name} (a sink) cannot appear right of a :=${lazyModule.line}") + require (pi.size >= iKnown, s"${name} (a sink) has ${iKnown} inputs out of ${pi.size}; cannot assign ${pi.size - iKnown} edges to resolve :*=${lazyModule.line}") + (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 diff --git a/src/main/scala/groundtest/Coreplex.scala b/src/main/scala/groundtest/Coreplex.scala index 60848787..e4b133d0 100644 --- a/src/main/scala/groundtest/Coreplex.scala +++ b/src/main/scala/groundtest/Coreplex.scala @@ -24,7 +24,7 @@ class GroundTestCoreplex(implicit p: Parameters) extends BaseCoreplex { case TileId => i case CacheBlockOffsetBits => log2Up(site(CacheBlockBytes)) case AmoAluOperandBits => site(XLen) - case SharedMemoryTLEdge => l1tol2.node.edgesIn(0) + case SharedMemoryTLEdge => tile_splitter.node.edgesIn(0) case TLId => "L1toL2" case TLKey("L1toL2") => TileLinkParameters( @@ -41,7 +41,7 @@ class GroundTestCoreplex(implicit p: Parameters) extends BaseCoreplex { } val fixer = LazyModule(new TLFIFOFixer) - l1tol2.node :=* fixer.node + tile_splitter.node :=* fixer.node tiles.foreach { fixer.node :=* _.masterNode } val cbusRAM = LazyModule(new TLRAM(AddressSet(testRamAddr, 0xffff), false, cbus_beatBytes)) diff --git a/src/main/scala/uncore/tilelink2/NodeNumberer.scala b/src/main/scala/uncore/tilelink2/NodeNumberer.scala new file mode 100644 index 00000000..60242bb2 --- /dev/null +++ b/src/main/scala/uncore/tilelink2/NodeNumberer.scala @@ -0,0 +1,73 @@ +// See LICENSE.SiFive for license details. + +package uncore.tilelink2 + +import Chisel._ +import chisel3.internal.sourceinfo.SourceInfo +import config._ +import diplomacy._ + +case class TLNodeNumbererNode(nodeAddressOffset: Option[Int] = None) extends TLCustomNode(0 to 999, 0 to 999) +{ + val externalIn = true + val externalOut = true + + def resolveStar(iKnown: Int, oKnown: Int, iStars: Int, oStars: Int): (Int, Int) = { + require (oStars + iStars <= 1, s"${name} (a custom adapter) appears left of a :*= ${iStars} times and right of a :=* ${oStars} times; at most once is allowed${lazyModule.line}") + if (oStars > 0) { + require (iKnown >= oKnown, s"${name} (a custom adapter) has ${oKnown} outputs and ${iKnown} inputs; cannot assign ${iKnown-oKnown} edges to resolve :=*${lazyModule.line}") + (0, iKnown - oKnown) + } else { + require (oKnown >= iKnown, s"${name} (a custom adapter) has ${oKnown} outputs and ${iKnown} inputs; cannot assign ${oKnown-iKnown} edges to resolve :*=${lazyModule.line}") + (oKnown - iKnown, 0) + } + } + + def mapParamsD(n: Int, p: Seq[TLClientPortParameters]): Seq[TLClientPortParameters] = { + require(n == p.size, s"${name} has ${p.size} inputs and ${n} outputs; they must match${lazyModule.line}") + p + } + + def mapParamsU(n: Int, p: Seq[TLManagerPortParameters]): Seq[TLManagerPortParameters] = { + require(n == p.size, s"${name} has ${n} inputs and ${p.size} outputs; they must match${lazyModule.line}") + val minNodeOffset = log2Ceil(p.map(_.maxAddress).max) + val nodeOffset = nodeAddressOffset.getOrElse(minNodeOffset) + require (nodeOffset >= minNodeOffset) + + p.zipWithIndex.map { case (mp, i) => + val nodeIndex = BigInt(i+1) << nodeOffset + mp.copy(managers = mp.managers.map(m => m.copy(address = m.address.map(a => a.copy(base = a.base | nodeIndex))))) + } + } +} + +class TLNodeNumberer(nodeAddressOffset: Option[Int] = None)(implicit p: Parameters) extends LazyModule +{ + val node = TLNodeNumbererNode(nodeAddressOffset) + + lazy val module = new LazyModuleImp(this) { + val io = new Bundle { + val in = node.bundleIn + val out = node.bundleOut + } + + val minNodeOffset = log2Ceil(node.edgesOut.map(_.manager.maxAddress).max) + val nodeOffset = nodeAddressOffset.getOrElse(minNodeOffset) + + (io.in zip io.out).zipWithIndex foreach { case ((in, out), i) => + out <> in + // a&c address already get truncated + in.b.bits.address := (UInt(i+1) << nodeOffset) | out.b.bits.address + } + } +} + +object TLNodeNumberer +{ + // applied to the TL source node; y.node := TLBuffer(x.node) + def apply(nodeAddressOffset: Option[Int] = None)(x: TLOutwardNode)(implicit p: Parameters, sourceInfo: SourceInfo): TLOutwardNode = { + val numberer = LazyModule(new TLNodeNumberer(nodeAddressOffset)) + numberer.node := x + numberer.node + } +} diff --git a/src/main/scala/uncore/tilelink2/Nodes.scala b/src/main/scala/uncore/tilelink2/Nodes.scala index 32d502b6..afeece78 100644 --- a/src/main/scala/uncore/tilelink2/Nodes.scala +++ b/src/main/scala/uncore/tilelink2/Nodes.scala @@ -92,6 +92,18 @@ case class TLNexusNode( numManagerPorts: Range.Inclusive = 1 to 999) extends NexusNode(TLImp)(clientFn, managerFn, numClientPorts, numManagerPorts) +case class TLSplitterNode( + clientFn: SplitterArg[TLClientPortParameters] => Seq[TLClientPortParameters], + managerFn: SplitterArg[TLManagerPortParameters] => Seq[TLManagerPortParameters], + numClientPorts: Range.Inclusive = 0 to 999, + numManagerPorts: Range.Inclusive = 0 to 999) + extends SplitterNode(TLImp)(clientFn, managerFn, numClientPorts, numManagerPorts) + +abstract class TLCustomNode( + numClientPorts: Range.Inclusive, + numManagerPorts: Range.Inclusive) + extends CustomNode(TLImp)(numClientPorts, numManagerPorts) + // Nodes passed from an inner module case class TLOutputNode() extends OutputNode(TLImp) case class TLInputNode() extends InputNode(TLImp) diff --git a/src/main/scala/uncore/tilelink2/Splitter.scala b/src/main/scala/uncore/tilelink2/Splitter.scala new file mode 100644 index 00000000..6a0a4503 --- /dev/null +++ b/src/main/scala/uncore/tilelink2/Splitter.scala @@ -0,0 +1,118 @@ +// See LICENSE.SiFive for license details. + +package uncore.tilelink2 + +import Chisel._ +import config._ +import diplomacy._ + +class TLSplitter(policy: TLArbiter.Policy = TLArbiter.lowestIndexFirst)(implicit p: Parameters) extends LazyModule +{ + val node = TLSplitterNode( + clientFn = { case SplitterArg(newSize, ports) => + if (newSize == 0) Nil else + Seq.fill(newSize / ports.size) { ports }.flatten + }, + managerFn = { case SplitterArg(newSize, ports) => + if (newSize == 0) Nil else + ports.grouped(newSize).toList.transpose.map { seq => + val fifoIdFactory = TLXbar.relabeler() + val outputIdRanges = TLXbar.mapOutputIds(seq) + seq(0).copy( + minLatency = seq.map(_.minLatency).min, + endSinkId = outputIdRanges.map(_.map(_.end).getOrElse(0)).max, + managers = ManagerUnification(seq.zipWithIndex.flatMap { case (port, i) => + require (port.beatBytes == seq(0).beatBytes, + s"Splitter data widths don't match: ${port.managers.map(_.name)} has ${port.beatBytes}B vs ${seq(0).managers.map(_.name)} has ${seq(0).beatBytes}B") + val fifoIdMapper = fifoIdFactory() + port.managers map { manager => manager.copy( + fifoId = manager.fifoId.map(fifoIdMapper(_)) + )} + }) + ) + } + }) + + lazy val module = new LazyModuleImp(this) { + val io = new Bundle { + val in = node.bundleIn + val out = node.bundleOut + } + + def group[T](x: Seq[T]) = + if (x.isEmpty) Nil else x.grouped(node.edgesIn.size).toList.transpose + + ((node.edgesIn zip io.in) zip (group(node.edgesOut) zip group(io.out))) foreach { + case ((edgeIn, io_in), (edgesOut, io_out)) => + + // Grab the port ID mapping + val outputIdRanges = TLXbar.mapOutputIds(edgesOut.map(_.manager)) + + // Find a good mask for address decoding + val port_addrs = edgesOut.map(_.manager.managers.map(_.address).flatten) + val routingMask = AddressDecoder(port_addrs) + val route_addrs = port_addrs.map(_.map(_.widen(~routingMask)).distinct) + val outputPorts = route_addrs.map(seq => (addr: UInt) => seq.map(_.contains(addr)).reduce(_ || _)) + + // We need an intermediate size of bundle with the widest possible identifiers + val wide_bundle = TLBundleParameters.union(Seq(io_in.params) ++ io_out.map(_.params)) + + // Transform input bundle sources (sinks use global namespace on both sides) + val in = Wire(TLBundle(wide_bundle)) + in.a <> io_in.a + io_in.d <> in.d + + if (edgeIn.client.anySupportProbe && edgesOut.exists(_.manager.anySupportAcquireB)) { + in.c <> io_in.c + in.e <> io_in.e + io_in.b <> in.b + } else { + in.c.valid := Bool(false) + in.e.valid := Bool(false) + in.b.ready := Bool(false) + io_in.c.ready := Bool(true) + io_in.e.ready := Bool(true) + io_in.b.valid := Bool(false) + } + + // Handle size = 1 gracefully (Chisel3 empty range is broken) + def trim(id: UInt, size: Int) = if (size <= 1) UInt(0) else id(log2Ceil(size)-1, 0) + + // Transform output bundle sinks (sources use global namespace on both sides) + val out = Wire(Vec(io_out.size, TLBundle(wide_bundle))) + for (i <- 0 until out.size) { + val r = outputIdRanges(i) + + io_out(i).a <> out(i).a + out(i).d <> io_out(i).d + out(i).d.bits.sink := io_out(i).d.bits.sink | UInt(r.map(_.start).getOrElse(0)) + + if (edgesOut(i).manager.anySupportAcquireB && edgeIn.client.anySupportProbe) { + io_out(i).c <> out(i).c + io_out(i).e <> out(i).e + out(i).b <> io_out(i).b + io_out(i).e.bits.sink := trim(out(i).e.bits.sink, r.map(_.size).getOrElse(0)) + } else { + out(i).c.ready := Bool(false) + out(i).e.ready := Bool(false) + out(i).b.valid := Bool(false) + io_out(i).c.valid := Bool(false) + io_out(i).e.valid := Bool(false) + io_out(i).b.ready := Bool(true) + } + } + + val requestA = Vec(outputPorts.map { o => o(in.a.bits.address) }) + val requestC = Vec(outputPorts.map { o => o(in.c.bits.address) }) + val requestE = Vec(outputIdRanges.map { o => o.map(_.contains(in.e.bits.sink)).getOrElse(Bool(false)) }) + (out.map(_.a) zip TLXbar.fanout(in.a, requestA)) foreach { case (o, i) => o <> i } + (out.map(_.c) zip TLXbar.fanout(in.c, requestC)) foreach { case (o, i) => o <> i } + (out.map(_.e) zip TLXbar.fanout(in.e, requestE)) foreach { case (o, i) => o <> i } + + val beatsB = Vec((out zip edgesOut) map { case (o, e) => e.numBeats1(o.b.bits) }) + val beatsD = Vec((out zip edgesOut) map { case (o, e) => e.numBeats1(o.d.bits) }) + TLArbiter(policy)(in.b, (beatsB zip out.map(_.b)):_*) + TLArbiter(policy)(in.d, (beatsD zip out.map(_.d)):_*) + } + } +} diff --git a/src/main/scala/uncore/tilelink2/Xbar.scala b/src/main/scala/uncore/tilelink2/Xbar.scala index b331fcad..b75fd14a 100644 --- a/src/main/scala/uncore/tilelink2/Xbar.scala +++ b/src/main/scala/uncore/tilelink2/Xbar.scala @@ -8,34 +8,6 @@ import diplomacy._ class TLXbar(policy: TLArbiter.Policy = TLArbiter.roundRobin)(implicit p: Parameters) extends LazyModule { - def mapInputIds (ports: Seq[TLClientPortParameters ]) = assignRanges(ports.map(_.endSourceId)).map(_.get) - def mapOutputIds(ports: Seq[TLManagerPortParameters]) = assignRanges(ports.map(_.endSinkId)) - - def assignRanges(sizes: Seq[Int]) = { - val pow2Sizes = sizes.map { z => if (z == 0) 0 else 1 << log2Ceil(z) } - val tuples = pow2Sizes.zipWithIndex.sortBy(_._1) // record old index, then sort by increasing size - val starts = tuples.scanRight(0)(_._1 + _).tail // suffix-sum of the sizes = the start positions - val ranges = (tuples zip starts) map { case ((sz, i), st) => - (if (sz == 0) None else Some(IdRange(st, st+sz)), i) - } - ranges.sortBy(_._2).map(_._1) // Restore orignal order - } - - def relabeler() = { - var idFactory = 0 - () => { - val fifoMap = scala.collection.mutable.HashMap.empty[Int, Int] - (x: Int) => { - if (fifoMap.contains(x)) fifoMap(x) else { - val out = idFactory - idFactory = idFactory + 1 - fifoMap += (x -> out) - out - } - } - } - } - val node = TLNexusNode( numClientPorts = 1 to 32, numManagerPorts = 1 to 32, @@ -44,7 +16,7 @@ class TLXbar(policy: TLArbiter.Policy = TLArbiter.roundRobin)(implicit p: Parame "An unsafe atomic port can not be combined with any other!") seq(0).copy( minLatency = seq.map(_.minLatency).min, - clients = (mapInputIds(seq) zip seq) flatMap { case (range, port) => + clients = (TLXbar.mapInputIds(seq) zip seq) flatMap { case (range, port) => port.clients map { client => client.copy( sourceId = client.sourceId.shift(range.start) )} @@ -52,8 +24,8 @@ class TLXbar(policy: TLArbiter.Policy = TLArbiter.roundRobin)(implicit p: Parame ) }, managerFn = { seq => - val fifoIdFactory = relabeler() - val outputIdRanges = mapOutputIds(seq) + val fifoIdFactory = TLXbar.relabeler() + val outputIdRanges = TLXbar.mapOutputIds(seq) seq(0).copy( minLatency = seq.map(_.minLatency).min, endSinkId = outputIdRanges.map(_.map(_.end).getOrElse(0)).max, @@ -75,8 +47,8 @@ class TLXbar(policy: TLArbiter.Policy = TLArbiter.roundRobin)(implicit p: Parame } // Grab the port ID mapping - val inputIdRanges = mapInputIds(node.edgesIn.map(_.client)) - val outputIdRanges = mapOutputIds(node.edgesOut.map(_.manager)) + val inputIdRanges = TLXbar.mapInputIds(node.edgesIn.map(_.client)) + val outputIdRanges = TLXbar.mapOutputIds(node.edgesOut.map(_.manager)) // Find a good mask for address decoding val port_addrs = node.edgesOut.map(_.manager.managers.map(_.address).flatten) @@ -105,7 +77,7 @@ class TLXbar(policy: TLArbiter.Policy = TLArbiter.roundRobin)(implicit p: Parame } // We need an intermediate size of bundle with the widest possible identifiers - val wide_bundle = io.in(0).params.union(io.out(0).params) + val wide_bundle = TLBundleParameters.union(io.in.map(_.params) ++ io.out.map(_.params)) // Handle size = 1 gracefully (Chisel3 empty range is broken) def trim(id: UInt, size: Int) = if (size <= 1) UInt(0) else id(log2Ceil(size)-1, 0) @@ -179,23 +151,12 @@ class TLXbar(policy: TLArbiter.Policy = TLArbiter.roundRobin)(implicit p: Parame def transpose[T](x: Seq[Seq[T]]) = Seq.tabulate(x(0).size) { i => Seq.tabulate(x.size) { j => x(j)(i) } } def filter[T](data: Seq[T], mask: Seq[Boolean]) = (data zip mask).filter(_._2).map(_._1) - // Replicate an input port to each output port - def fanout[T <: TLChannel](input: DecoupledIO[T], select: Seq[Bool]) = { - val filtered = Wire(Vec(select.size, input)) - for (i <- 0 until select.size) { - filtered(i).bits := input.bits - filtered(i).valid := input.valid && select(i) - } - input.ready := Mux1H(select, filtered.map(_.ready)) - filtered - } - // Fanout the input sources to the output sinks - val portsAOI = transpose((in zip requestAIO) map { case (i, r) => fanout(i.a, r) }) - val portsBIO = transpose((out zip requestBOI) map { case (o, r) => fanout(o.b, r) }) - val portsCOI = transpose((in zip requestCIO) map { case (i, r) => fanout(i.c, r) }) - val portsDIO = transpose((out zip requestDOI) map { case (o, r) => fanout(o.d, r) }) - val portsEOI = transpose((in zip requestEIO) map { case (i, r) => fanout(i.e, r) }) + val portsAOI = transpose((in zip requestAIO) map { case (i, r) => TLXbar.fanout(i.a, r) }) + val portsBIO = transpose((out zip requestBOI) map { case (o, r) => TLXbar.fanout(o.b, r) }) + val portsCOI = transpose((in zip requestCIO) map { case (i, r) => TLXbar.fanout(i.c, r) }) + val portsDIO = transpose((out zip requestDOI) map { case (o, r) => TLXbar.fanout(o.d, r) }) + val portsEOI = transpose((in zip requestEIO) map { case (i, r) => TLXbar.fanout(i.e, r) }) // Arbitrate amongst the sources for (o <- 0 until out.size) { @@ -219,6 +180,48 @@ class TLXbar(policy: TLArbiter.Policy = TLArbiter.roundRobin)(implicit p: Parame } } +object TLXbar +{ + def mapInputIds (ports: Seq[TLClientPortParameters ]) = assignRanges(ports.map(_.endSourceId)).map(_.get) + def mapOutputIds(ports: Seq[TLManagerPortParameters]) = assignRanges(ports.map(_.endSinkId)) + + def assignRanges(sizes: Seq[Int]) = { + val pow2Sizes = sizes.map { z => if (z == 0) 0 else 1 << log2Ceil(z) } + val tuples = pow2Sizes.zipWithIndex.sortBy(_._1) // record old index, then sort by increasing size + val starts = tuples.scanRight(0)(_._1 + _).tail // suffix-sum of the sizes = the start positions + val ranges = (tuples zip starts) map { case ((sz, i), st) => + (if (sz == 0) None else Some(IdRange(st, st+sz)), i) + } + ranges.sortBy(_._2).map(_._1) // Restore orignal order + } + + def relabeler() = { + var idFactory = 0 + () => { + val fifoMap = scala.collection.mutable.HashMap.empty[Int, Int] + (x: Int) => { + if (fifoMap.contains(x)) fifoMap(x) else { + val out = idFactory + idFactory = idFactory + 1 + fifoMap += (x -> out) + out + } + } + } + } + + // Replicate an input port to each output port + def fanout[T <: TLChannel](input: DecoupledIO[T], select: Seq[Bool]) = { + val filtered = Wire(Vec(select.size, input)) + for (i <- 0 until select.size) { + filtered(i).bits := input.bits + filtered(i).valid := input.valid && select(i) + } + input.ready := Mux1H(select, filtered.map(_.ready)) + filtered + } +} + /** Synthesizeable unit tests */ import unittest._