Skip to content

Commit

Permalink
[rtl] initial implementation of crossbar
Browse files Browse the repository at this point in the history
  • Loading branch information
OceanS2000 committed Feb 15, 2023
1 parent 817dab6 commit deca900
Show file tree
Hide file tree
Showing 3 changed files with 402 additions and 0 deletions.
261 changes: 261 additions & 0 deletions tilelink/src/xbar/TLCrossBar.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2016-2017 SiFive, Inc. See LICENSE.SiFive for details

package xbar

import chisel3._
import chisel3.util._
import chisel3.util.experimental.BitSet
import chisel3.util.experimental.decode.decoder

import bundle._

object TLCrossBar {
private def fanout(
input: DecoupledIO[TLChannel],
select: Seq[Bool]
): Seq[DecoupledIO[TLChannel]] = {
val filtered = Wire(Vec(select.size, chiselTypeOf(input)))
filtered.zip(select).foreach { case (chan, selected) =>
chan.bits := input.bits
chan.valid := input.valid && (selected || (select.size == 1).B)
}
input.ready := Mux1H(select, filtered.map(_.ready))
filtered
}
}

class TLCrossBar(val parameter: TLCrossBarParameter)
extends Module
with chisel3.experimental.SerializableModule[TLCrossBarParameter] {

val masterLinksIO = parameter.masters.map(_.linkParameter).map { link =>
IO(Flipped(new TLLink(link)))
}
val slaveLinksIO = parameter.slaves.map(_.linkParameter).map { link =>
IO(new TLLink(link))
}

val masterLinksRemapped = Wire(
Vec(parameter.masters.size, new TLLink(parameter.commonLinkParameter))
)
val slaveLinksRemapped = Wire(
Vec(parameter.slaves.size, new TLLink(parameter.commonLinkParameter))
)

private def trim(id: UInt, size: Int): UInt = id(log2Ceil(size) - 1, 0)

// id remapping
parameter.adReachableIO
.lazyZip(masterLinksIO)
.lazyZip(masterLinksRemapped)
.lazyZip(parameter.srcIdRemapTable)
.foreach { case (connects, io, remapped, range) =>
if (connects.exists(x => x)) {
remapped.a :<>= io.a
remapped.a.bits.source := io.a.bits.source | range.start.U

io.d :<>= remapped.d
io.d.bits.source := trim(remapped.d.bits.source, range.size)
} else {
remapped.a.valid := false.B
remapped.a.bits := DontCare
io.a.ready := false.B
io.a.bits := DontCare

io.d.valid := false.B
io.d.bits := DontCare
remapped.d.ready := false.B
remapped.d.bits := DontCare
}
}
parameter.bceReachableIO
.lazyZip(masterLinksIO)
.lazyZip(masterLinksRemapped)
.lazyZip(parameter.srcIdRemapTable)
.foreach { case (connects, io, remapped, range) =>
if (connects.exists(x => x)) {
io.b :<>= remapped.b
io.b.bits.source := trim(remapped.b.bits.source, range.size)

remapped.c :<>= io.c
remapped.c.bits.source := io.c.bits.source | range.start.U

remapped.e :<>= io.e
} else {
io.b.valid := false.B
io.b.bits := DontCare
remapped.b.ready := false.B
remapped.b.bits := DontCare

remapped.c.valid := false.B
remapped.c.bits := DontCare
io.c.ready := false.B
io.c.bits := DontCare

remapped.e.valid := false.B
remapped.e.bits := DontCare
io.e.ready := false.B
io.e.bits := DontCare
}
}

parameter.adReachableOI
.lazyZip(slaveLinksIO)
.lazyZip(slaveLinksRemapped)
.lazyZip(parameter.sinkIdRemapTable)
.foreach { case (connects, io, remapped, range) =>
if (connects.exists(x => x)) {
remapped.a :<>= io.a

io.d :<>= remapped.d
io.d.bits.sink := trim(remapped.d.bits.sink, range.size)
} else {
remapped.a.valid := false.B
remapped.a.bits := DontCare
io.a.ready := false.B
io.a.bits := DontCare

io.d.valid := false.B
io.d.bits := DontCare
remapped.d.ready := false.B
remapped.d.bits := DontCare
}
}

parameter.bceReachableOI
.lazyZip(slaveLinksIO)
.lazyZip(slaveLinksRemapped)
.lazyZip(parameter.sinkIdRemapTable)
.foreach { case (connects, io, remapped, range) =>
if (connects.exists(x => x)) {
io.b :<>= remapped.b
remapped.c :<>= io.c
remapped.e :<>= io.e

remapped.e.bits.sink := io.e.bits.sink | range.start.U
} else {
io.b.valid := false.B
io.b.bits := DontCare
remapped.b.ready := false.B
remapped.b.bits := DontCare

remapped.c.valid := false.B
remapped.c.bits := DontCare
io.c.ready := false.B
io.c.bits := DontCare

remapped.e.valid := false.B
remapped.e.bits := DontCare
io.e.ready := false.B
io.e.bits := DontCare
}
}

private def unique(x: Vector[Boolean]) = x.count(x => x) <= 1
private def filter[T](data: Seq[T], mask: Seq[Boolean]) = data.zip(mask).filter(_._2).map(_._1)

// Based on input=>output connectivity, create per-input minimal address decode circuits
val addressableOs = (parameter.adReachableIO ++ parameter.bceReachableIO).distinct
val outputPortFns: Map[Vector[Boolean], UInt => Seq[Bool]] =
addressableOs.map { addressable =>
if (unique(addressable)) {
(addressable, (_: UInt) => addressable.map(_.B))
} else {
val ports = parameter.slaves.map(_.addressRange)
val maxBits = log2Ceil(1 + ports.map(_.getWidth).max)
val maskedPorts = ports.zip(addressable).map {
case (port, true) => port.intersect(BitPat.dontCare(maxBits))
case (_, false) => BitSet.empty
}
//noinspection RedundantDefaultArgument
(addressable, (addr: UInt) => decoder.bitset(addr, maskedPorts, errorBit = false).asBools)
}
}.toMap

val addressA = masterLinksRemapped.map(_.a.bits.address)
val addressC = masterLinksRemapped.map(_.c.bits.address)

val requestAIO = parameter.adReachableIO.zip(addressA).map { case (c, a) => outputPortFns(c)(a) }
val requestCIO = parameter.bceReachableIO.zip(addressC).map { case (c, a) => outputPortFns(c)(a) }
val requestBOI = slaveLinksRemapped.map { o => parameter.srcIdRemapTable.map { i => i.contains(o.b.bits.source) } }
val requestDOI = slaveLinksRemapped.map { o => parameter.srcIdRemapTable.map { i => i.contains(o.d.bits.source) } }
val requestEIO = masterLinksRemapped.map { i => parameter.sinkIdRemapTable.map { o => o.contains(i.e.bits.sink) } }

val portsAOI = masterLinksRemapped.zip(requestAIO).map { case (i, r) => TLCrossBar.fanout(i.a, r) }.transpose
val portsBIO = slaveLinksRemapped.zip(requestBOI).map { case (o, r) => TLCrossBar.fanout(o.b, r) }.transpose
val portsCOI = masterLinksRemapped.zip(requestCIO).map { case (i, r) => TLCrossBar.fanout(i.c, r) }.transpose
val portsDIO = slaveLinksRemapped.zip(requestDOI).map { case (o, r) => TLCrossBar.fanout(o.d, r) }.transpose
val portsEOI = masterLinksRemapped.zip(requestEIO).map { case (i, r) => TLCrossBar.fanout(i.e, r) }.transpose

slaveLinksRemapped.lazyZip(portsAOI).lazyZip(parameter.adReachableOI).foreach { case (portO, portI, reachable) =>
val arbiter = Module(
new TLArbiter(
TLArbiterParameter(
policy = parameter.arbitrationPolicy,
inputLinkParameters = filter(portI.map(_.bits.parameter), reachable),
outputLinkParameter = portO.a.bits.parameter
)
)
)
arbiter.sources.zip(filter(portI, reachable)).foreach { case (o, i) => o :<>= i }
portO.a :<>= arbiter.sink.asInstanceOf[DecoupledIO[TLChannelA]]
filter(portI, reachable.map(!_)).foreach { i => i.ready := false.B }
}
masterLinksRemapped.lazyZip(portsBIO).lazyZip(parameter.bceReachableIO).foreach { case (portI, portO, reachable) =>
val arbiter = Module(
new TLArbiter(
TLArbiterParameter(
policy = parameter.arbitrationPolicy,
inputLinkParameters = filter(portO.map(_.bits.parameter), reachable),
outputLinkParameter = portI.b.bits.parameter
)
)
)
arbiter.sources.zip(filter(portO, reachable)).foreach { case (i, o) => i :<>= o }
portI.b :<>= arbiter.sink.asInstanceOf[DecoupledIO[TLChannelB]]
filter(portO, reachable.map(!_)).foreach { o => o.ready := false.B }
}
slaveLinksRemapped.lazyZip(portsCOI).lazyZip(parameter.bceReachableOI).foreach { case (portO, portI, reachable) =>
val arbiter = Module(
new TLArbiter(
TLArbiterParameter(
policy = parameter.arbitrationPolicy,
inputLinkParameters = filter(portI.map(_.bits.parameter), reachable),
outputLinkParameter = portO.c.bits.parameter
)
)
)
arbiter.sources.zip(filter(portI, reachable)).foreach { case (o, i) => o :<>= i }
portO.c :<>= arbiter.sink.asInstanceOf[DecoupledIO[TLChannelC]]
filter(portI, reachable.map(!_)).foreach { i => i.ready := false.B }
}
masterLinksRemapped.lazyZip(portsDIO).lazyZip(parameter.adReachableIO).foreach { case (portI, portO, reachable) =>
val arbiter = Module(
new TLArbiter(
TLArbiterParameter(
policy = parameter.arbitrationPolicy,
inputLinkParameters = filter(portO.map(_.bits.parameter), reachable),
outputLinkParameter = portI.d.bits.parameter
)
)
)
arbiter.sources.zip(filter(portO, reachable)).foreach { case (i, o) => i :<>= o }
portI.d :<>= arbiter.sink.asInstanceOf[DecoupledIO[TLChannelD]]
filter(portO, reachable.map(!_)).foreach { o => o.ready := false.B }
}
slaveLinksRemapped.lazyZip(portsEOI).lazyZip(parameter.bceReachableOI).foreach { case (portO, portI, reachable) =>
val arbiter = Module(
new TLArbiter(
TLArbiterParameter(
policy = parameter.arbitrationPolicy,
inputLinkParameters = filter(portI.map(_.bits.parameter), reachable),
outputLinkParameter = portO.e.bits.parameter
)
)
)
arbiter.sources.zip(filter(portI, reachable)).foreach { case (o, i) => o :<>= i }
portO.e :<>= arbiter.sink.asInstanceOf[DecoupledIO[TLChannelE]]
filter(portI, reachable.map(!_)).foreach { i => i.ready := false.B }
}
}
81 changes: 81 additions & 0 deletions tilelink/src/xbar/TLCrossBarParameters.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2016-2017 SiFive, Inc. See LICENSE.SiFive for details

package xbar

import bundle._

import chisel3.util.log2Ceil
import upickle.default.{macroRW, readwriter, ReadWriter => RW}

case class TLCrossBarMasterLinkParameter(
linkParameter: TLLinkParameter,
adVisibility: chisel3.util.experimental.BitSet,
bceVisibility: chisel3.util.experimental.BitSet,
srcIdRange: TLIdRange)
object TLCrossBarMasterLinkParameter {
import utils.Serializers._
implicit val rw: RW[TLCrossBarMasterLinkParameter] = macroRW
}

case class TLCrossBarSlaveLinkParameter(
linkParameter: TLLinkParameter,
adVisibility: chisel3.util.experimental.BitSet,
bceVisibility: chisel3.util.experimental.BitSet,
sinkIdRange: TLIdRange,
addressRange: chisel3.util.experimental.BitSet)
object TLCrossBarSlaveLinkParameter {
import utils.Serializers._
implicit val rw: RW[TLCrossBarSlaveLinkParameter] = macroRW
}

case class TLCrossBarParameter(
arbitrationPolicy: TLArbiterPolicy,
masters: Seq[TLCrossBarMasterLinkParameter],
slaves: Seq[TLCrossBarSlaveLinkParameter])
extends chisel3.experimental.SerializableModuleParameter {

slaves.map(_.addressRange).combinations(2).foreach { case Seq(a, b) =>
require(!a.overlap(b), s"Address ranges of different slaves cannot overlap: $a, $b.")
}

private[tilelink] def adReachableIO = masters.map { case TLCrossBarMasterLinkParameter(_, visibility, _, _) =>
slaves.map { case TLCrossBarSlaveLinkParameter(_, severability, _, _, _) =>
visibility.overlap(severability)
}.toVector
}.toVector
private[tilelink] def bceReachableIO = masters.map { case TLCrossBarMasterLinkParameter(_, _, visibility, _) =>
slaves.map { case TLCrossBarSlaveLinkParameter(_, _, severability, _, _) =>
visibility.overlap(severability)
}.toVector
}.toVector

private[tilelink] def adReachableOI = adReachableIO.transpose
private[tilelink] def bceReachableOI = bceReachableIO.transpose
private[tilelink] def srcIdRemapTable =
TLCrossBarParameter.assignIdRange(masters.map(_.srcIdRange.end))
private[tilelink] def sinkIdRemapTable =
TLCrossBarParameter.assignIdRange(slaves.map(_.sinkIdRange.end))
private[tilelink] def commonLinkParameter = TLLinkParameter.union(
masters.map(_.linkParameter) ++ slaves.map(_.linkParameter): _*
)
}

object TLCrossBarParameter {
private def assignIdRange(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) TLIdRange(0, 0) else TLIdRange(st, st + sz), i)
}
ranges.sortBy(_._2).map(_._1) // Restore original order
}

implicit val rw: RW[TLCrossBarParameter] = macroRW
}
Loading

0 comments on commit deca900

Please sign in to comment.