Merge pull request #757 from freechipsproject/isp-port
Inter-System-Port
This commit is contained in:
commit
5368ea60fe
@ -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
|
||||
|
44
src/main/scala/coreplex/ISPPort.scala
Normal file
44
src/main/scala/coreplex/ISPPort.scala
Normal file
@ -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
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
73
src/main/scala/uncore/tilelink2/NodeNumberer.scala
Normal file
73
src/main/scala/uncore/tilelink2/NodeNumberer.scala
Normal file
@ -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
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
118
src/main/scala/uncore/tilelink2/Splitter.scala
Normal file
118
src/main/scala/uncore/tilelink2/Splitter.scala
Normal file
@ -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)):_*)
|
||||
}
|
||||
}
|
||||
}
|
@ -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._
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user