2016-11-28 01:16:37 +01:00
|
|
|
// See LICENSE.SiFive for license details.
|
2016-08-24 22:50:32 +02:00
|
|
|
|
|
|
|
package uncore.tilelink2
|
|
|
|
|
|
|
|
import Chisel._
|
2016-12-02 02:46:52 +01:00
|
|
|
import config._
|
2016-10-04 00:17:36 +02:00
|
|
|
import diplomacy._
|
2016-08-24 22:50:32 +02:00
|
|
|
|
2016-12-02 02:46:52 +01:00
|
|
|
class TLXbar(policy: TLArbiter.Policy = TLArbiter.lowestIndexFirst)(implicit p: Parameters) extends LazyModule
|
2016-08-24 22:50:32 +02:00
|
|
|
{
|
2017-03-24 02:19:04 +01:00
|
|
|
def mapInputIds (ports: Seq[TLClientPortParameters ]) = assignRanges(ports.map(_.endSourceId)).map(_.get)
|
2016-08-24 22:50:32 +02:00
|
|
|
def mapOutputIds(ports: Seq[TLManagerPortParameters]) = assignRanges(ports.map(_.endSinkId))
|
|
|
|
|
|
|
|
def assignRanges(sizes: Seq[Int]) = {
|
2017-03-24 02:19:04 +01:00
|
|
|
val pow2Sizes = sizes.map { z => if (z == 0) 0 else 1 << log2Ceil(z) }
|
2016-08-24 22:50:32 +02:00
|
|
|
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
|
2017-03-24 02:19:04 +01:00
|
|
|
val ranges = (tuples zip starts) map { case ((sz, i), st) =>
|
|
|
|
(if (sz == 0) None else Some(IdRange(st, st+sz)), i)
|
|
|
|
}
|
2016-08-24 22:50:32 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-30 00:17:52 +01:00
|
|
|
val node = TLNexusNode(
|
2016-08-24 22:50:32 +02:00
|
|
|
numClientPorts = 1 to 32,
|
|
|
|
numManagerPorts = 1 to 32,
|
|
|
|
clientFn = { seq =>
|
2017-03-24 05:55:11 +01:00
|
|
|
require (!seq.exists(_.unsafeAtomics) || seq.size == 1,
|
|
|
|
"An unsafe atomic port can not be combined with any other!")
|
2016-09-22 02:26:52 +02:00
|
|
|
seq(0).copy(
|
|
|
|
minLatency = seq.map(_.minLatency).min,
|
|
|
|
clients = (mapInputIds(seq) zip seq) flatMap { case (range, port) =>
|
|
|
|
port.clients map { client => client.copy(
|
|
|
|
sourceId = client.sourceId.shift(range.start)
|
|
|
|
)}
|
|
|
|
}
|
|
|
|
)
|
2016-08-24 22:50:32 +02:00
|
|
|
},
|
|
|
|
managerFn = { seq =>
|
|
|
|
val fifoIdFactory = relabeler()
|
2016-11-03 01:53:32 +01:00
|
|
|
val outputIdRanges = mapOutputIds(seq)
|
2016-09-22 02:26:52 +02:00
|
|
|
seq(0).copy(
|
|
|
|
minLatency = seq.map(_.minLatency).min,
|
2017-03-24 02:19:04 +01:00
|
|
|
endSinkId = outputIdRanges.map(_.map(_.end).getOrElse(0)).max,
|
2016-11-04 03:05:53 +01:00
|
|
|
managers = ManagerUnification(seq.flatMap { port =>
|
2017-03-24 05:55:11 +01:00
|
|
|
require (port.beatBytes == seq(0).beatBytes,
|
|
|
|
s"Xbar data widths don't match: ${port.managers.map(_.name)} has ${port.beatBytes}B vs ${seq(0).managers.map(_.name)} has ${seq(0).beatBytes}B")
|
2016-09-22 02:26:52 +02:00
|
|
|
val fifoIdMapper = fifoIdFactory()
|
|
|
|
port.managers map { manager => manager.copy(
|
|
|
|
fifoId = manager.fifoId.map(fifoIdMapper(_))
|
|
|
|
)}
|
2016-11-04 03:05:53 +01:00
|
|
|
})
|
2016-09-22 02:26:52 +02:00
|
|
|
)
|
2016-08-24 22:50:32 +02:00
|
|
|
})
|
|
|
|
|
2016-09-02 20:13:43 +02:00
|
|
|
lazy val module = new LazyModuleImp(this) {
|
2016-08-24 22:50:32 +02:00
|
|
|
val io = new Bundle {
|
|
|
|
val in = node.bundleIn
|
|
|
|
val out = node.bundleOut
|
|
|
|
}
|
|
|
|
|
|
|
|
// Grab the port ID mapping
|
|
|
|
val inputIdRanges = mapInputIds(node.edgesIn.map(_.client))
|
|
|
|
val outputIdRanges = mapOutputIds(node.edgesOut.map(_.manager))
|
|
|
|
|
2016-09-18 01:13:46 +02:00
|
|
|
// Find a good mask for address decoding
|
|
|
|
val port_addrs = node.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(_ || _))
|
|
|
|
|
|
|
|
// Print the mapping
|
|
|
|
if (false) {
|
|
|
|
println("Xbar mapping:")
|
|
|
|
route_addrs.foreach { p =>
|
|
|
|
print(" ")
|
|
|
|
p.foreach { a => print(s" ${a}") }
|
|
|
|
println("")
|
|
|
|
}
|
|
|
|
println("--")
|
|
|
|
}
|
|
|
|
|
2016-08-24 22:50:32 +02:00
|
|
|
// We need an intermediate size of bundle with the widest possible identifiers
|
|
|
|
val wide_bundle = io.in(0).params.union(io.out(0).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)
|
|
|
|
|
|
|
|
// Transform input bundle sources (sinks use global namespace on both sides)
|
|
|
|
val in = Wire(Vec(io.in.size, TLBundle(wide_bundle)))
|
|
|
|
for (i <- 0 until in.size) {
|
|
|
|
val r = inputIdRanges(i)
|
2017-03-20 01:02:24 +01:00
|
|
|
|
|
|
|
in(i).a <> io.in(i).a
|
|
|
|
io.in(i).d <> in(i).d
|
2016-08-24 22:50:32 +02:00
|
|
|
in(i).a.bits.source := io.in(i).a.bits.source | UInt(r.start)
|
|
|
|
io.in(i).d.bits.source := trim(in(i).d.bits.source, r.size)
|
2017-03-20 01:02:24 +01:00
|
|
|
|
|
|
|
if (node.edgesIn(i).client.anySupportProbe && node.edgesOut.exists(_.manager.anySupportAcquireB)) {
|
|
|
|
in(i).c <> io.in(i).c
|
|
|
|
in(i).e <> io.in(i).e
|
|
|
|
io.in(i).b <> in(i).b
|
|
|
|
in(i).c.bits.source := io.in(i).c.bits.source | UInt(r.start)
|
|
|
|
io.in(i).b.bits.source := trim(in(i).b.bits.source, r.size)
|
|
|
|
} else {
|
|
|
|
in(i).c.valid := Bool(false)
|
|
|
|
in(i).e.valid := Bool(false)
|
|
|
|
in(i).b.ready := Bool(false)
|
|
|
|
io.in(i).c.ready := Bool(true)
|
|
|
|
io.in(i).e.ready := Bool(true)
|
|
|
|
io.in(i).b.valid := Bool(false)
|
|
|
|
}
|
2016-08-24 22:50:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
2017-03-20 01:02:24 +01:00
|
|
|
|
|
|
|
io.out(i).a <> out(i).a
|
|
|
|
out(i).d <> io.out(i).d
|
2017-03-24 02:19:04 +01:00
|
|
|
out(i).d.bits.sink := io.out(i).d.bits.sink | UInt(r.map(_.start).getOrElse(0))
|
2017-03-20 01:02:24 +01:00
|
|
|
|
|
|
|
if (node.edgesOut(i).manager.anySupportAcquireB && node.edgesIn.exists(_.client.anySupportProbe)) {
|
|
|
|
io.out(i).c <> out(i).c
|
|
|
|
io.out(i).e <> out(i).e
|
|
|
|
out(i).b <> io.out(i).b
|
2017-03-24 02:19:04 +01:00
|
|
|
io.out(i).e.bits.sink := trim(out(i).e.bits.sink, r.map(_.size).getOrElse(0))
|
2017-03-20 01:02:24 +01:00
|
|
|
} 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)
|
|
|
|
}
|
2016-08-24 22:50:32 +02:00
|
|
|
}
|
|
|
|
|
2016-09-24 01:24:29 +02:00
|
|
|
val addressA = (in zip node.edgesIn) map { case (i, e) => e.address(i.a.bits) }
|
|
|
|
val addressC = (in zip node.edgesIn) map { case (i, e) => e.address(i.c.bits) }
|
2016-08-24 22:50:32 +02:00
|
|
|
|
2016-09-24 01:24:29 +02:00
|
|
|
val requestAIO = Vec(addressA.map { i => Vec(outputPorts.map { o => o(i) }) })
|
|
|
|
val requestCIO = Vec(addressC.map { i => Vec(outputPorts.map { o => o(i) }) })
|
|
|
|
val requestBOI = Vec(out.map { o => Vec(inputIdRanges.map { i => i.contains(o.b.bits.source) }) })
|
|
|
|
val requestDOI = Vec(out.map { o => Vec(inputIdRanges.map { i => i.contains(o.d.bits.source) }) })
|
2017-03-24 02:19:04 +01:00
|
|
|
val requestEIO = Vec(in.map { i => Vec(outputIdRanges.map { o => o.map(_.contains(i.e.bits.sink)).getOrElse(Bool(false)) }) })
|
2016-08-24 22:50:32 +02:00
|
|
|
|
2016-10-13 03:35:16 +02:00
|
|
|
val beatsAI = Vec((in zip node.edgesIn) map { case (i, e) => e.numBeats1(i.a.bits) })
|
|
|
|
val beatsBO = Vec((out zip node.edgesOut) map { case (o, e) => e.numBeats1(o.b.bits) })
|
|
|
|
val beatsCI = Vec((in zip node.edgesIn) map { case (i, e) => e.numBeats1(i.c.bits) })
|
|
|
|
val beatsDO = Vec((out zip node.edgesOut) map { case (o, e) => e.numBeats1(o.d.bits) })
|
|
|
|
val beatsEI = Vec((in zip node.edgesIn) map { case (i, e) => e.numBeats1(i.e.bits) })
|
2016-08-24 22:50:32 +02:00
|
|
|
|
2016-09-24 01:24:29 +02:00
|
|
|
// Which pairs support support transfers
|
|
|
|
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
|
2016-10-13 03:09:01 +02:00
|
|
|
def fanout[T <: TLChannel](input: DecoupledIO[T], select: Seq[Bool]) = {
|
2016-09-24 01:24:29 +02:00
|
|
|
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
|
2016-08-24 22:50:32 +02:00
|
|
|
}
|
|
|
|
|
2016-09-24 01:24:29 +02:00
|
|
|
// 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) })
|
2016-08-24 22:50:32 +02:00
|
|
|
|
2016-09-24 01:24:29 +02:00
|
|
|
// Arbitrate amongst the sources
|
|
|
|
for (o <- 0 until out.size) {
|
|
|
|
val allowI = Seq.tabulate(in.size) { i =>
|
|
|
|
node.edgesIn(i).client.anySupportProbe &&
|
2017-01-18 03:52:16 +01:00
|
|
|
node.edgesOut(o).manager.anySupportAcquireB
|
2016-08-24 22:50:32 +02:00
|
|
|
}
|
2016-09-24 01:24:29 +02:00
|
|
|
TLArbiter(policy)(out(o).a, (beatsAI zip portsAOI(o) ):_*)
|
|
|
|
TLArbiter(policy)(out(o).c, filter(beatsCI zip portsCOI(o), allowI):_*)
|
|
|
|
TLArbiter(policy)(out(o).e, filter(beatsEI zip portsEOI(o), allowI):_*)
|
2016-08-24 22:50:32 +02:00
|
|
|
}
|
|
|
|
|
2016-09-24 01:24:29 +02:00
|
|
|
for (i <- 0 until in.size) {
|
|
|
|
val allowO = Seq.tabulate(out.size) { o =>
|
|
|
|
node.edgesIn(i).client.anySupportProbe &&
|
2017-01-18 03:52:16 +01:00
|
|
|
node.edgesOut(o).manager.anySupportAcquireB
|
2016-09-24 01:24:29 +02:00
|
|
|
}
|
|
|
|
TLArbiter(policy)(in(i).b, filter(beatsBO zip portsBIO(i), allowO):_*)
|
|
|
|
TLArbiter(policy)(in(i).d, (beatsDO zip portsDIO(i) ):_*)
|
2016-08-24 22:50:32 +02:00
|
|
|
}
|
2016-09-02 20:13:43 +02:00
|
|
|
}
|
2016-08-24 22:50:32 +02:00
|
|
|
}
|
2016-09-29 00:11:05 +02:00
|
|
|
|
|
|
|
/** Synthesizeable unit tests */
|
|
|
|
import unittest._
|
|
|
|
|
2016-12-02 02:46:52 +01:00
|
|
|
class TLRAMXbar(nManagers: Int)(implicit p: Parameters) extends LazyModule {
|
2016-09-29 00:11:05 +02:00
|
|
|
val fuzz = LazyModule(new TLFuzzer(5000))
|
2017-04-13 20:51:10 +02:00
|
|
|
val model = LazyModule(new TLRAMModel("Xbar"))
|
2016-09-29 00:11:05 +02:00
|
|
|
val xbar = LazyModule(new TLXbar)
|
|
|
|
|
|
|
|
model.node := fuzz.node
|
2017-03-11 02:10:41 +01:00
|
|
|
xbar.node := TLDelayer(0.1)(model.node)
|
2016-09-29 00:11:05 +02:00
|
|
|
(0 until nManagers) foreach { n =>
|
|
|
|
val ram = LazyModule(new TLRAM(AddressSet(0x0+0x400*n, 0x3ff)))
|
2017-03-11 02:10:41 +01:00
|
|
|
ram.node := TLFragmenter(4, 256)(TLDelayer(0.1)(xbar.node))
|
2016-09-29 00:11:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
lazy val module = new LazyModuleImp(this) with HasUnitTestIO {
|
|
|
|
io.finished := fuzz.module.io.finished
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-17 20:37:23 +02:00
|
|
|
class TLRAMXbarTest(nManagers: Int, timeout: Int = 500000)(implicit p: Parameters) extends UnitTest(timeout) {
|
2016-09-29 00:11:05 +02:00
|
|
|
io.finished := Module(LazyModule(new TLRAMXbar(nManagers)).module).io.finished
|
|
|
|
}
|
|
|
|
|
2016-12-02 02:46:52 +01:00
|
|
|
class TLMulticlientXbar(nManagers: Int, nClients: Int)(implicit p: Parameters) extends LazyModule {
|
2016-09-29 00:11:05 +02:00
|
|
|
val xbar = LazyModule(new TLXbar)
|
|
|
|
|
|
|
|
val fuzzers = (0 until nClients) map { n =>
|
|
|
|
val fuzz = LazyModule(new TLFuzzer(5000))
|
2017-03-11 02:10:41 +01:00
|
|
|
xbar.node := TLDelayer(0.1)(fuzz.node)
|
2016-09-29 00:11:05 +02:00
|
|
|
fuzz
|
|
|
|
}
|
|
|
|
|
|
|
|
(0 until nManagers) foreach { n =>
|
|
|
|
val ram = LazyModule(new TLRAM(AddressSet(0x0+0x400*n, 0x3ff)))
|
2017-03-11 02:10:41 +01:00
|
|
|
ram.node := TLFragmenter(4, 256)(TLDelayer(0.1)(xbar.node))
|
2016-09-29 00:11:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
lazy val module = new LazyModuleImp(this) with HasUnitTestIO {
|
|
|
|
io.finished := fuzzers.last.module.io.finished
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-17 20:37:23 +02:00
|
|
|
class TLMulticlientXbarTest(nManagers: Int, nClients: Int, timeout: Int = 500000)(implicit p: Parameters) extends UnitTest(timeout) {
|
2016-09-29 00:11:05 +02:00
|
|
|
io.finished := Module(LazyModule(new TLMulticlientXbar(nManagers, nClients)).module).io.finished
|
|
|
|
}
|