Merge pull request #536 from ucb-bar/diplomacy-star-nodes
diplomacy: add :*= and :=* to support flexible # of edges
This commit is contained in:
commit
b567a2a356
@ -11,17 +11,14 @@ import util._
|
|||||||
|
|
||||||
class DefaultCoreplex(implicit p: Parameters) extends BaseCoreplex
|
class DefaultCoreplex(implicit p: Parameters) extends BaseCoreplex
|
||||||
with CoreplexRISCVPlatform
|
with CoreplexRISCVPlatform
|
||||||
with HasL2MasterPort
|
|
||||||
with HasRocketTiles {
|
with HasRocketTiles {
|
||||||
override lazy val module = new DefaultCoreplexModule(this, () => new DefaultCoreplexBundle(this))
|
override lazy val module = new DefaultCoreplexModule(this, () => new DefaultCoreplexBundle(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
class DefaultCoreplexBundle[+L <: DefaultCoreplex](_outer: L) extends BaseCoreplexBundle(_outer)
|
class DefaultCoreplexBundle[+L <: DefaultCoreplex](_outer: L) extends BaseCoreplexBundle(_outer)
|
||||||
with CoreplexRISCVPlatformBundle
|
with CoreplexRISCVPlatformBundle
|
||||||
with HasL2MasterPortBundle
|
|
||||||
with HasRocketTilesBundle
|
with HasRocketTilesBundle
|
||||||
|
|
||||||
class DefaultCoreplexModule[+L <: DefaultCoreplex, +B <: DefaultCoreplexBundle[L]](_outer: L, _io: () => B) extends BaseCoreplexModule(_outer, _io)
|
class DefaultCoreplexModule[+L <: DefaultCoreplex, +B <: DefaultCoreplexBundle[L]](_outer: L, _io: () => B) extends BaseCoreplexModule(_outer, _io)
|
||||||
with CoreplexRISCVPlatformModule
|
with CoreplexRISCVPlatformModule
|
||||||
with HasL2MasterPortModule
|
|
||||||
with HasRocketTilesModule
|
with HasRocketTilesModule
|
||||||
|
@ -24,9 +24,13 @@ trait CoreplexNetwork extends HasCoreplexParameters {
|
|||||||
|
|
||||||
val mmio = TLOutputNode()
|
val mmio = TLOutputNode()
|
||||||
val mmioInt = IntInputNode()
|
val mmioInt = IntInputNode()
|
||||||
|
val l2in = TLInputNode()
|
||||||
|
|
||||||
intBar.intnode := mmioInt
|
intBar.intnode := mmioInt
|
||||||
|
|
||||||
|
// Allows a variable number of inputs from outside to the Xbar
|
||||||
|
l1tol2.node :=* l2in
|
||||||
|
|
||||||
cbus.node :=
|
cbus.node :=
|
||||||
TLBuffer()(
|
TLBuffer()(
|
||||||
TLAtomicAutomata(arithmetic = true)( // disable once TLB uses TL2 metadata
|
TLAtomicAutomata(arithmetic = true)( // disable once TLB uses TL2 metadata
|
||||||
@ -43,6 +47,7 @@ trait CoreplexNetworkBundle extends HasCoreplexParameters {
|
|||||||
|
|
||||||
val mmio = outer.mmio.bundleOut
|
val mmio = outer.mmio.bundleOut
|
||||||
val interrupts = outer.mmioInt.bundleIn
|
val interrupts = outer.mmioInt.bundleIn
|
||||||
|
val l2in = outer.l2in.bundleIn
|
||||||
}
|
}
|
||||||
|
|
||||||
trait CoreplexNetworkModule extends HasCoreplexParameters {
|
trait CoreplexNetworkModule extends HasCoreplexParameters {
|
||||||
@ -93,21 +98,3 @@ trait BankedL2CoherenceManagersModule extends CoreplexNetworkModule {
|
|||||||
val outer: BankedL2CoherenceManagers
|
val outer: BankedL2CoherenceManagers
|
||||||
val io: BankedL2CoherenceManagersBundle
|
val io: BankedL2CoherenceManagersBundle
|
||||||
}
|
}
|
||||||
|
|
||||||
/////
|
|
||||||
|
|
||||||
trait HasL2MasterPort extends CoreplexNetwork {
|
|
||||||
val module: HasL2MasterPortModule
|
|
||||||
val l2in = TLInputNode()
|
|
||||||
l1tol2.node := TLBuffer()(l2in)
|
|
||||||
}
|
|
||||||
|
|
||||||
trait HasL2MasterPortBundle extends CoreplexNetworkBundle {
|
|
||||||
val outer: HasL2MasterPort
|
|
||||||
val l2in = outer.l2in.bundleIn
|
|
||||||
}
|
|
||||||
|
|
||||||
trait HasL2MasterPortModule extends CoreplexNetworkModule {
|
|
||||||
val outer: HasL2MasterPort
|
|
||||||
val io: HasL2MasterPortBundle
|
|
||||||
}
|
|
||||||
|
@ -16,7 +16,9 @@ trait InwardNodeImp[DI, UI, EI, BI <: Data]
|
|||||||
def edgeI(pd: DI, pu: UI): EI
|
def edgeI(pd: DI, pu: UI): EI
|
||||||
def bundleI(ei: Seq[EI]): Vec[BI]
|
def bundleI(ei: Seq[EI]): Vec[BI]
|
||||||
def colour: String
|
def colour: String
|
||||||
def connect(bo: => BI, bi: => BI, e: => EI)(implicit p: Parameters, sourceInfo: SourceInfo): (Option[LazyModule], () => Unit)
|
def connect(bindings: () => Seq[(EI, BI, BI)])(implicit p: Parameters, sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = {
|
||||||
|
(None, () => bindings().foreach { case (_, i, o) => i <> o })
|
||||||
|
}
|
||||||
|
|
||||||
// optional methods to track node graph
|
// optional methods to track node graph
|
||||||
def mixI(pu: UI, node: InwardNode[DI, UI, BI]): UI = pu // insert node into parameters
|
def mixI(pu: UI, node: InwardNode[DI, UI, BI]): UI = pu // insert node into parameters
|
||||||
@ -71,8 +73,17 @@ trait InwardNodeHandle[DI, UI, BI <: Data]
|
|||||||
val inward: InwardNode[DI, UI, BI]
|
val inward: InwardNode[DI, UI, BI]
|
||||||
def := (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): Option[LazyModule] =
|
def := (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): Option[LazyModule] =
|
||||||
inward.:=(h)(p, sourceInfo)
|
inward.:=(h)(p, sourceInfo)
|
||||||
|
def :*= (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): Option[LazyModule] =
|
||||||
|
inward.:*=(h)(p, sourceInfo)
|
||||||
|
def :=* (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): Option[LazyModule] =
|
||||||
|
inward.:=*(h)(p, sourceInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed trait NodeBinding
|
||||||
|
case object BIND_ONCE extends NodeBinding
|
||||||
|
case object BIND_QUERY extends NodeBinding
|
||||||
|
case object BIND_STAR extends NodeBinding
|
||||||
|
|
||||||
trait InwardNode[DI, UI, BI <: Data] extends BaseNode with InwardNodeHandle[DI, UI, BI]
|
trait InwardNode[DI, UI, BI <: Data] extends BaseNode with InwardNodeHandle[DI, UI, BI]
|
||||||
{
|
{
|
||||||
val inward = this
|
val inward = this
|
||||||
@ -81,21 +92,22 @@ trait InwardNode[DI, UI, BI <: Data] extends BaseNode with InwardNodeHandle[DI,
|
|||||||
require (!numPI.isEmpty, s"No number of inputs would be acceptable to ${name}${lazyModule.line}")
|
require (!numPI.isEmpty, s"No number of inputs would be acceptable to ${name}${lazyModule.line}")
|
||||||
require (numPI.start >= 0, s"${name} accepts a negative number of inputs${lazyModule.line}")
|
require (numPI.start >= 0, s"${name} accepts a negative number of inputs${lazyModule.line}")
|
||||||
|
|
||||||
private val accPI = ListBuffer[(Int, OutwardNode[DI, UI, BI])]()
|
private val accPI = ListBuffer[(Int, OutwardNode[DI, UI, BI], NodeBinding)]()
|
||||||
private var iRealized = false
|
private var iRealized = false
|
||||||
|
|
||||||
protected[diplomacy] def iPushed = accPI.size
|
protected[diplomacy] def iPushed = accPI.size
|
||||||
protected[diplomacy] def iPush(index: Int, node: OutwardNode[DI, UI, BI])(implicit sourceInfo: SourceInfo) {
|
protected[diplomacy] def iPush(index: Int, node: OutwardNode[DI, UI, BI], binding: NodeBinding)(implicit sourceInfo: SourceInfo) {
|
||||||
val info = sourceLine(sourceInfo, " at ", "")
|
val info = sourceLine(sourceInfo, " at ", "")
|
||||||
val noIs = numPI.size == 1 && numPI.contains(0)
|
val noIs = numPI.size == 1 && numPI.contains(0)
|
||||||
require (!noIs, s"${name}${lazyModule.line} was incorrectly connected as a sink" + info)
|
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)
|
require (!iRealized, s"${name}${lazyModule.line} was incorrectly connected as a sink after it's .module was used" + info)
|
||||||
accPI += ((index, node))
|
accPI += ((index, node, binding))
|
||||||
}
|
}
|
||||||
|
|
||||||
private def reqI() = require(numPI.contains(accPI.size), s"${name} has ${accPI.size} inputs, expected ${numPI}${lazyModule.line}")
|
protected[diplomacy] lazy val iBindings = { iRealized = true; accPI.result() }
|
||||||
protected[diplomacy] lazy val iPorts = { iRealized = true; reqI(); accPI.result() }
|
|
||||||
|
|
||||||
|
protected[diplomacy] val iStar: Int
|
||||||
|
protected[diplomacy] val iPortMapping: Seq[(Int, Int)]
|
||||||
protected[diplomacy] val iParams: Seq[UI]
|
protected[diplomacy] val iParams: Seq[UI]
|
||||||
val bundleIn: Vec[BI]
|
val bundleIn: Vec[BI]
|
||||||
}
|
}
|
||||||
@ -113,48 +125,84 @@ trait OutwardNode[DO, UO, BO <: Data] extends BaseNode with OutwardNodeHandle[DO
|
|||||||
require (!numPO.isEmpty, s"No number of outputs would be acceptable to ${name}${lazyModule.line}")
|
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}")
|
require (numPO.start >= 0, s"${name} accepts a negative number of outputs${lazyModule.line}")
|
||||||
|
|
||||||
private val accPO = ListBuffer[(Int, InwardNode [DO, UO, BO])]()
|
private val accPO = ListBuffer[(Int, InwardNode [DO, UO, BO], NodeBinding)]()
|
||||||
private var oRealized = false
|
private var oRealized = false
|
||||||
|
|
||||||
protected[diplomacy] def oPushed = accPO.size
|
protected[diplomacy] def oPushed = accPO.size
|
||||||
protected[diplomacy] def oPush(index: Int, node: InwardNode [DO, UO, BO])(implicit sourceInfo: SourceInfo) {
|
protected[diplomacy] def oPush(index: Int, node: InwardNode [DO, UO, BO], binding: NodeBinding)(implicit sourceInfo: SourceInfo) {
|
||||||
val info = sourceLine(sourceInfo, " at ", "")
|
val info = sourceLine(sourceInfo, " at ", "")
|
||||||
val noOs = numPO.size == 1 && numPO.contains(0)
|
val noOs = numPO.size == 1 && numPO.contains(0)
|
||||||
require (!noOs, s"${name}${lazyModule.line} was incorrectly connected as a source" + info)
|
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)
|
require (!oRealized, s"${name}${lazyModule.line} was incorrectly connected as a source after it's .module was used" + info)
|
||||||
accPO += ((index, node))
|
accPO += ((index, node, binding))
|
||||||
}
|
}
|
||||||
|
|
||||||
private def reqO() = require(numPO.contains(accPO.size), s"${name} has ${accPO.size} outputs, expected ${numPO}${lazyModule.line}")
|
protected[diplomacy] lazy val oBindings = { oRealized = true; accPO.result() }
|
||||||
protected[diplomacy] lazy val oPorts = { oRealized = true; reqO(); accPO.result() }
|
|
||||||
|
|
||||||
|
protected[diplomacy] val oStar: Int
|
||||||
|
protected[diplomacy] val oPortMapping: Seq[(Int, Int)]
|
||||||
protected[diplomacy] val oParams: Seq[DO]
|
protected[diplomacy] val oParams: Seq[DO]
|
||||||
val bundleOut: Vec[BO]
|
val bundleOut: Vec[BO]
|
||||||
}
|
}
|
||||||
|
|
||||||
class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data](
|
abstract class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data](
|
||||||
inner: InwardNodeImp [DI, UI, EI, BI],
|
inner: InwardNodeImp [DI, UI, EI, BI],
|
||||||
outer: OutwardNodeImp[DO, UO, EO, BO])(
|
outer: OutwardNodeImp[DO, UO, EO, BO])(
|
||||||
private val dFn: (Int, Seq[DI]) => Seq[DO],
|
|
||||||
private val uFn: (Int, Seq[UO]) => Seq[UI],
|
|
||||||
protected[diplomacy] val numPO: Range.Inclusive,
|
protected[diplomacy] val numPO: Range.Inclusive,
|
||||||
protected[diplomacy] val numPI: Range.Inclusive)
|
protected[diplomacy] val numPI: Range.Inclusive)
|
||||||
extends BaseNode with InwardNode[DI, UI, BI] with OutwardNode[DO, UO, BO]
|
extends BaseNode with InwardNode[DI, UI, BI] with OutwardNode[DO, UO, BO]
|
||||||
{
|
{
|
||||||
// meta-data for printing the node graph
|
protected[diplomacy] def resolveStarO(i: Int, o: Int): Int
|
||||||
protected[diplomacy] def colour = inner.colour
|
protected[diplomacy] def resolveStarI(i: Int, o: Int): Int
|
||||||
protected[diplomacy] def outputs = oPorts.map(_._2) zip edgesOut.map(e => outer.labelO(e))
|
protected[diplomacy] def mapParamsD(n: Int, p: Seq[DI]): Seq[DO]
|
||||||
protected[diplomacy] def inputs = iPorts.map(_._2) zip edgesIn .map(e => inner.labelI(e))
|
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
|
||||||
|
case BIND_STAR => 0 }}.foldLeft(0)(_+_)
|
||||||
|
val iKnown = iBindings.map { case (_, n, b) => b match {
|
||||||
|
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 oSum = oBindings.map { case (_, n, b) => b match {
|
||||||
|
case BIND_ONCE => 1
|
||||||
|
case BIND_QUERY => n.iStar
|
||||||
|
case BIND_STAR => oStar }}.scanLeft(0)(_+_)
|
||||||
|
val iSum = iBindings.map { case (_, n, b) => b match {
|
||||||
|
case BIND_ONCE => 1
|
||||||
|
case BIND_QUERY => n.oStar
|
||||||
|
case BIND_STAR => iStar }}.scanLeft(0)(_+_)
|
||||||
|
val oTotal = oSum.lastOption.getOrElse(0)
|
||||||
|
val iTotal = iSum.lastOption.getOrElse(0)
|
||||||
|
require(numPO.contains(oTotal), s"${name} has ${oTotal} outputs, expected ${numPO}${lazyModule.line}")
|
||||||
|
require(numPI.contains(iTotal), s"${name} has ${iTotal} inputs, expected ${numPI}${lazyModule.line}")
|
||||||
|
(oSum.init zip oSum.tail, iSum.init zip iSum.tail, oStar, iStar)
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy val oPorts = oBindings.flatMap { case (i, n, _) =>
|
||||||
|
val (start, end) = n.iPortMapping(i)
|
||||||
|
(start until end) map { j => (j, n) }
|
||||||
|
}
|
||||||
|
lazy val iPorts = iBindings.flatMap { case (i, n, _) =>
|
||||||
|
val (start, end) = n.oPortMapping(i)
|
||||||
|
(start until end) map { j => (j, n) }
|
||||||
|
}
|
||||||
|
|
||||||
private def reqE(o: Int, i: Int) = require(i == o, s"${name} has ${i} inputs and ${o} outputs; they must match${lazyModule.line}")
|
|
||||||
protected[diplomacy] lazy val oParams: Seq[DO] = {
|
protected[diplomacy] lazy val oParams: Seq[DO] = {
|
||||||
val o = dFn(oPorts.size, iPorts.map { case (i, n) => n.oParams(i) })
|
val o = mapParamsD(oPorts.size, iPorts.map { case (i, n) => n.oParams(i) })
|
||||||
reqE(oPorts.size, o.size)
|
require (o.size == oPorts.size, s"Bug in diplomacy; ${name} has ${o.size} != ${oPorts.size} down/up outer parameters${lazyModule.line}")
|
||||||
o.map(outer.mixO(_, this))
|
o.map(outer.mixO(_, this))
|
||||||
}
|
}
|
||||||
protected[diplomacy] lazy val iParams: Seq[UI] = {
|
protected[diplomacy] lazy val iParams: Seq[UI] = {
|
||||||
val i = uFn(iPorts.size, oPorts.map { case (o, n) => n.iParams(o) })
|
val i = mapParamsU(iPorts.size, oPorts.map { case (o, n) => n.iParams(o) })
|
||||||
reqE(i.size, iPorts.size)
|
require (i.size == iPorts.size, s"Bug in diplomacy; ${name} has ${i.size} != ${iPorts.size} up/down inner parameters${lazyModule.line}")
|
||||||
i.map(inner.mixI(_, this))
|
i.map(inner.mixI(_, this))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,87 +223,174 @@ class MixedNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data](
|
|||||||
lazy val bundleIn = wireI(flipI(inner.bundleI(edgesIn)))
|
lazy val bundleIn = wireI(flipI(inner.bundleI(edgesIn)))
|
||||||
|
|
||||||
// connects the outward part of a node with the inward part of this node
|
// connects the outward part of a node with the inward part of this node
|
||||||
override def := (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): Option[LazyModule] = {
|
private def bind(h: OutwardNodeHandle[DI, UI, BI], binding: NodeBinding)(implicit p: Parameters, sourceInfo: SourceInfo): Option[LazyModule] = {
|
||||||
val x = this // x := y
|
val x = this // x := y
|
||||||
val y = h.outward
|
val y = h.outward
|
||||||
val info = sourceLine(sourceInfo, " at ", "")
|
val info = sourceLine(sourceInfo, " at ", "")
|
||||||
require (!LazyModule.stack.isEmpty, s"${y.name} cannot be connected to ${x.name} outside of LazyModule scope" + info)
|
require (!LazyModule.stack.isEmpty, s"${y.name} cannot be connected to ${x.name} outside of LazyModule scope" + info)
|
||||||
val i = x.iPushed
|
val i = x.iPushed
|
||||||
val o = y.oPushed
|
val o = y.oPushed
|
||||||
y.oPush(i, x)
|
y.oPush(i, x, binding match {
|
||||||
x.iPush(o, y)
|
case BIND_ONCE => BIND_ONCE
|
||||||
val (out, binding) = inner.connect(y.bundleOut(o), x.bundleIn(i), x.edgesIn(i))
|
case BIND_STAR => BIND_QUERY
|
||||||
LazyModule.stack.head.bindings = binding :: LazyModule.stack.head.bindings
|
case BIND_QUERY => BIND_STAR })
|
||||||
|
x.iPush(o, y, binding)
|
||||||
|
def bindings() = {
|
||||||
|
val (iStart, iEnd) = x.iPortMapping(i)
|
||||||
|
val (oStart, oEnd) = y.oPortMapping(o)
|
||||||
|
require (iEnd - iStart == oEnd - oStart, s"Bug in diplomacy; ${iEnd-iStart} != ${oEnd-oStart} means port resolution failed")
|
||||||
|
Seq.tabulate(iEnd - iStart) { j =>
|
||||||
|
(x.edgesIn(iStart+j), x.bundleIn(iStart+j), y.bundleOut(oStart+j))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val (out, newbinding) = inner.connect(bindings _)
|
||||||
|
LazyModule.stack.head.bindings = newbinding :: LazyModule.stack.head.bindings
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def := (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): Option[LazyModule] = bind(h, BIND_ONCE)
|
||||||
|
override def :*= (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): Option[LazyModule] = bind(h, BIND_STAR)
|
||||||
|
override def :=* (h: OutwardNodeHandle[DI, UI, BI])(implicit p: Parameters, sourceInfo: SourceInfo): Option[LazyModule] = bind(h, BIND_QUERY)
|
||||||
|
|
||||||
|
// meta-data for printing the node graph
|
||||||
|
protected[diplomacy] def colour = inner.colour
|
||||||
|
protected[diplomacy] def outputs = oPorts.map(_._2) zip edgesOut.map(e => outer.labelO(e))
|
||||||
|
protected[diplomacy] def inputs = iPorts.map(_._2) zip edgesIn .map(e => inner.labelI(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
class MixedAdapterNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data](
|
||||||
|
inner: InwardNodeImp [DI, UI, EI, BI],
|
||||||
|
outer: OutwardNodeImp[DO, UO, EO, BO])(
|
||||||
|
dFn: DI => DO,
|
||||||
|
uFn: UO => UI,
|
||||||
|
num: Range.Inclusive = 0 to 999)
|
||||||
|
extends MixedNode(inner, outer)(num, num)
|
||||||
|
{
|
||||||
|
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 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}")
|
||||||
|
p.map(dFn)
|
||||||
|
}
|
||||||
|
protected[diplomacy] def mapParamsU(n: Int, p: Seq[UO]): Seq[UI] = {
|
||||||
|
require(n == p.size, s"${name} has ${n} inputs and ${p.size} outputs; they must match${lazyModule.line}")
|
||||||
|
p.map(uFn)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SimpleNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(
|
class MixedNexusNode[DI, UI, EI, BI <: Data, DO, UO, EO, BO <: Data](
|
||||||
oFn: (Int, Seq[D]) => Seq[D],
|
inner: InwardNodeImp [DI, UI, EI, BI],
|
||||||
iFn: (Int, Seq[U]) => Seq[U],
|
outer: OutwardNodeImp[DO, UO, EO, BO])(
|
||||||
numPO: Range.Inclusive,
|
dFn: Seq[DI] => DO,
|
||||||
numPI: Range.Inclusive)
|
uFn: Seq[UO] => UI,
|
||||||
extends MixedNode[D, U, EI, B, D, U, EO, B](imp, imp)(oFn, iFn, numPO, numPI)
|
numPO: Range.Inclusive = 1 to 999,
|
||||||
|
numPI: Range.Inclusive = 1 to 999)
|
||||||
|
extends MixedNode(inner, outer)(numPO, numPI)
|
||||||
|
{
|
||||||
|
require (numPO.end >= 1, s"${name} does not accept outputs${lazyModule.line}")
|
||||||
|
require (numPI.end >= 1, s"${name} does not accept inputs${lazyModule.line}")
|
||||||
|
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 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) }
|
||||||
|
}
|
||||||
|
|
||||||
class IdentityNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])
|
class AdapterNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(
|
||||||
extends SimpleNode(imp)({case (_, s) => s}, {case (_, s) => s}, 0 to 999, 0 to 999)
|
dFn: D => D,
|
||||||
|
uFn: U => U,
|
||||||
|
num: Range.Inclusive = 0 to 999)
|
||||||
|
extends MixedAdapterNode[D, U, EI, B, D, U, EO, B](imp, imp)(dFn, uFn, num)
|
||||||
|
|
||||||
class OutputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) extends IdentityNode(imp)
|
class NexusNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(
|
||||||
|
dFn: Seq[D] => D,
|
||||||
|
uFn: Seq[U] => U,
|
||||||
|
numPO: Range.Inclusive = 1 to 999,
|
||||||
|
numPI: Range.Inclusive = 1 to 999)
|
||||||
|
extends MixedNexusNode[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})
|
||||||
|
|
||||||
|
class OutputNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B]) extends IdentityNode(imp)
|
||||||
{
|
{
|
||||||
override lazy val bundleIn = bundleOut
|
override lazy val bundleIn = bundleOut
|
||||||
}
|
}
|
||||||
|
|
||||||
class InputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B]) extends IdentityNode(imp)
|
class InputNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B]) extends IdentityNode(imp)
|
||||||
{
|
{
|
||||||
override lazy val bundleOut = bundleIn
|
override lazy val bundleOut = bundleIn
|
||||||
}
|
}
|
||||||
|
|
||||||
class SourceNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(po: PO, num: Range.Inclusive = 1 to 1)
|
class SourceNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(po: Seq[D])
|
||||||
extends SimpleNode(imp)({case (n, Seq()) => Seq.fill(n)(po)}, {case (0, _) => Seq()}, num, 0 to 0)
|
extends MixedNode(imp, imp)(po.size to po.size, 0 to 0)
|
||||||
{
|
{
|
||||||
require (num.end >= 1, s"${name} is a source which does not accept outputs${lazyModule.line}")
|
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 mapParamsD(n: Int, p: Seq[D]): Seq[D] = po
|
||||||
|
protected[diplomacy] def mapParamsU(n: Int, p: Seq[U]): Seq[U] = Seq()
|
||||||
|
|
||||||
override lazy val bundleIn = { require(false, s"${name} has no bundleIn; try bundleOut?"); bundleOut }
|
override lazy val bundleIn = { require(false, s"${name} has no bundleIn; try bundleOut?"); bundleOut }
|
||||||
}
|
}
|
||||||
|
|
||||||
class SinkNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(pi: PI, num: Range.Inclusive = 1 to 1)
|
class SinkNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(pi: Seq[U])
|
||||||
extends SimpleNode(imp)({case (0, _) => Seq()}, {case (n, Seq()) => Seq.fill(n)(pi)}, 0 to 0, num)
|
extends MixedNode(imp, imp)(0 to 0, pi.size to pi.size)
|
||||||
{
|
{
|
||||||
require (num.end >= 1, s"${name} is a sink which does not accept inputs${lazyModule.line}")
|
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 mapParamsD(n: Int, p: Seq[D]): Seq[D] = Seq()
|
||||||
|
protected[diplomacy] def mapParamsU(n: Int, p: Seq[U]): Seq[U] = pi
|
||||||
|
|
||||||
override lazy val bundleOut = { require(false, s"${name} has no bundleOut; try bundleIn?"); bundleIn }
|
override lazy val bundleOut = { require(false, s"${name} has no bundleOut; try bundleIn?"); bundleIn }
|
||||||
}
|
}
|
||||||
|
|
||||||
class BlindOutputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(pi: Seq[PI])
|
class BlindOutputNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(pi: Seq[U])
|
||||||
extends SimpleNode(imp)({case (0, _) => Seq()}, {case (_, Seq()) => pi}, 0 to 0, pi.size to pi.size)
|
extends SinkNode(imp)(pi)
|
||||||
{
|
{
|
||||||
override val flip = true
|
override val flip = true
|
||||||
override lazy val bundleOut = bundleIn
|
override lazy val bundleOut = bundleIn
|
||||||
}
|
}
|
||||||
|
|
||||||
class BlindInputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(po: Seq[PO])
|
class BlindInputNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(po: Seq[D])
|
||||||
extends SimpleNode(imp)({case (_, Seq()) => po}, {case (0, _) => Seq()}, po.size to po.size, 0 to 0)
|
extends SourceNode(imp)(po)
|
||||||
{
|
{
|
||||||
override val flip = true
|
override val flip = true
|
||||||
override lazy val bundleIn = bundleOut
|
override lazy val bundleIn = bundleOut
|
||||||
}
|
}
|
||||||
|
|
||||||
class InternalOutputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(pi: Seq[PI])
|
class InternalOutputNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(pi: Seq[U])
|
||||||
extends SimpleNode(imp)({case (0, _) => Seq()}, {case (_, Seq()) => pi}, 0 to 0, pi.size to pi.size)
|
extends SinkNode(imp)(pi)
|
||||||
{
|
{
|
||||||
override val wire = true
|
override val wire = true
|
||||||
override lazy val bundleOut = bundleIn
|
override lazy val bundleOut = bundleIn
|
||||||
}
|
}
|
||||||
|
|
||||||
class InternalInputNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])(po: Seq[PO])
|
class InternalInputNode[D, U, EO, EI, B <: Data](imp: NodeImp[D, U, EO, EI, B])(po: Seq[D])
|
||||||
extends SimpleNode(imp)({case (_, Seq()) => po}, {case (0, _) => Seq()}, po.size to po.size, 0 to 0)
|
extends SourceNode(imp)(po)
|
||||||
{
|
{
|
||||||
override val wire = true
|
override val wire = true
|
||||||
override lazy val bundleIn = bundleOut
|
override lazy val bundleIn = bundleOut
|
||||||
}
|
}
|
||||||
|
|
||||||
class InteriorNode[PO, PI, EO, EI, B <: Data](imp: NodeImp[PO, PI, EO, EI, B])
|
|
||||||
(oFn: Seq[PO] => PO, iFn: Seq[PI] => PI, numPO: Range.Inclusive, numPI: Range.Inclusive)
|
|
||||||
extends SimpleNode(imp)({case (n,s) => Seq.fill(n)(oFn(s))}, {case (n,s) => Seq.fill(n)(iFn(s))}, numPO, numPI)
|
|
||||||
{
|
|
||||||
require (numPO.end >= 1, s"${name} is an adapter which does not accept outputs${lazyModule.line}")
|
|
||||||
require (numPI.end >= 1, s"${name} is an adapter which does not accept inputs${lazyModule.line}")
|
|
||||||
}
|
|
||||||
|
@ -13,7 +13,7 @@ import uncore.util._
|
|||||||
|
|
||||||
class ScratchpadSlavePort(implicit p: Parameters) extends LazyModule {
|
class ScratchpadSlavePort(implicit p: Parameters) extends LazyModule {
|
||||||
val coreDataBytes = p(XLen)/8
|
val coreDataBytes = p(XLen)/8
|
||||||
val node = TLManagerNode(TLManagerPortParameters(
|
val node = TLManagerNode(Seq(TLManagerPortParameters(
|
||||||
Seq(TLManagerParameters(
|
Seq(TLManagerParameters(
|
||||||
address = List(AddressSet(0x80000000L, BigInt(p(DataScratchpadSize)-1))),
|
address = List(AddressSet(0x80000000L, BigInt(p(DataScratchpadSize)-1))),
|
||||||
regionType = RegionType.UNCACHED,
|
regionType = RegionType.UNCACHED,
|
||||||
@ -25,7 +25,7 @@ class ScratchpadSlavePort(implicit p: Parameters) extends LazyModule {
|
|||||||
supportsGet = TransferSizes(1, coreDataBytes),
|
supportsGet = TransferSizes(1, coreDataBytes),
|
||||||
fifoId = Some(0))), // requests handled in FIFO order
|
fifoId = Some(0))), // requests handled in FIFO order
|
||||||
beatBytes = coreDataBytes,
|
beatBytes = coreDataBytes,
|
||||||
minLatency = 1))
|
minLatency = 1)))
|
||||||
|
|
||||||
lazy val module = new LazyModuleImp(this) {
|
lazy val module = new LazyModuleImp(this) {
|
||||||
val io = new Bundle {
|
val io = new Bundle {
|
||||||
|
@ -34,6 +34,7 @@ trait TopNetwork extends HasPeripheryParameters {
|
|||||||
val socBus = LazyModule(new TLXbar)
|
val socBus = LazyModule(new TLXbar)
|
||||||
val peripheryBus = LazyModule(new TLXbar)
|
val peripheryBus = LazyModule(new TLXbar)
|
||||||
val intBus = LazyModule(new IntXbar)
|
val intBus = LazyModule(new IntXbar)
|
||||||
|
val l2 = LazyModule(new TLBuffer)
|
||||||
|
|
||||||
peripheryBus.node :=
|
peripheryBus.node :=
|
||||||
TLBuffer()(
|
TLBuffer()(
|
||||||
@ -62,13 +63,3 @@ class BaseTopBundle[+L <: BaseTop](_outer: L) extends BareTopBundle(_outer)
|
|||||||
|
|
||||||
class BaseTopModule[+L <: BaseTop, +B <: BaseTopBundle[L]](_outer: L, _io: () => B) extends BareTopModule(_outer, _io)
|
class BaseTopModule[+L <: BaseTop, +B <: BaseTopBundle[L]](_outer: L, _io: () => B) extends BareTopModule(_outer, _io)
|
||||||
with TopNetworkModule
|
with TopNetworkModule
|
||||||
|
|
||||||
trait L2Crossbar extends TopNetwork {
|
|
||||||
val l2 = LazyModule(new TLXbar)
|
|
||||||
}
|
|
||||||
|
|
||||||
trait L2CrossbarBundle extends TopNetworkBundle {
|
|
||||||
}
|
|
||||||
|
|
||||||
trait L2CrossbarModule extends TopNetworkModule {
|
|
||||||
}
|
|
||||||
|
@ -165,7 +165,7 @@ trait PeripheryMasterAXI4MMIOModule {
|
|||||||
/////
|
/////
|
||||||
|
|
||||||
// PeripherySlaveAXI4 is an example, make your own cake pattern like this one.
|
// PeripherySlaveAXI4 is an example, make your own cake pattern like this one.
|
||||||
trait PeripherySlaveAXI4 extends L2Crossbar {
|
trait PeripherySlaveAXI4 extends TopNetwork {
|
||||||
private val config = p(ExtIn)
|
private val config = p(ExtIn)
|
||||||
val l2_axi4 = AXI4BlindInputNode(Seq(AXI4MasterPortParameters(
|
val l2_axi4 = AXI4BlindInputNode(Seq(AXI4MasterPortParameters(
|
||||||
masters = Seq(AXI4MasterParameters(
|
masters = Seq(AXI4MasterParameters(
|
||||||
@ -179,12 +179,12 @@ trait PeripherySlaveAXI4 extends L2Crossbar {
|
|||||||
l2_axi4))))
|
l2_axi4))))
|
||||||
}
|
}
|
||||||
|
|
||||||
trait PeripherySlaveAXI4Bundle extends L2CrossbarBundle {
|
trait PeripherySlaveAXI4Bundle extends TopNetworkBundle {
|
||||||
val outer: PeripherySlaveAXI4
|
val outer: PeripherySlaveAXI4
|
||||||
val l2_axi4 = outer.l2_axi4.bundleIn
|
val l2_axi4 = outer.l2_axi4.bundleIn
|
||||||
}
|
}
|
||||||
|
|
||||||
trait PeripherySlaveAXI4Module extends L2CrossbarModule {
|
trait PeripherySlaveAXI4Module extends TopNetworkModule {
|
||||||
val outer: PeripherySlaveAXI4
|
val outer: PeripherySlaveAXI4
|
||||||
val io: PeripherySlaveAXI4Bundle
|
val io: PeripherySlaveAXI4Bundle
|
||||||
// nothing to do
|
// nothing to do
|
||||||
@ -231,7 +231,7 @@ trait PeripheryMasterTLMMIOModule {
|
|||||||
/////
|
/////
|
||||||
|
|
||||||
// NOTE: this port is NOT allowed to issue Acquires
|
// NOTE: this port is NOT allowed to issue Acquires
|
||||||
trait PeripherySlaveTL extends L2Crossbar {
|
trait PeripherySlaveTL extends TopNetwork {
|
||||||
private val config = p(ExtIn)
|
private val config = p(ExtIn)
|
||||||
val l2_tl = TLBlindInputNode(Seq(TLClientPortParameters(
|
val l2_tl = TLBlindInputNode(Seq(TLClientPortParameters(
|
||||||
clients = Seq(TLClientParameters(
|
clients = Seq(TLClientParameters(
|
||||||
@ -243,12 +243,12 @@ trait PeripherySlaveTL extends L2Crossbar {
|
|||||||
l2_tl))
|
l2_tl))
|
||||||
}
|
}
|
||||||
|
|
||||||
trait PeripherySlaveTLBundle extends L2CrossbarBundle {
|
trait PeripherySlaveTLBundle extends TopNetworkBundle {
|
||||||
val outer: PeripherySlaveTL
|
val outer: PeripherySlaveTL
|
||||||
val l2_tl = outer.l2_tl.bundleIn
|
val l2_tl = outer.l2_tl.bundleIn
|
||||||
}
|
}
|
||||||
|
|
||||||
trait PeripherySlaveTLModule extends L2CrossbarModule {
|
trait PeripherySlaveTLModule extends TopNetworkModule {
|
||||||
val outer: PeripherySlaveTL
|
val outer: PeripherySlaveTL
|
||||||
val io: PeripherySlaveTLBundle
|
val io: PeripherySlaveTLBundle
|
||||||
// nothing to do
|
// nothing to do
|
||||||
|
@ -10,23 +10,23 @@ import uncore.devices._
|
|||||||
import util._
|
import util._
|
||||||
import coreplex._
|
import coreplex._
|
||||||
|
|
||||||
trait RocketPlexMaster extends L2Crossbar {
|
trait RocketPlexMaster extends TopNetwork {
|
||||||
val module: RocketPlexMasterModule
|
val module: RocketPlexMasterModule
|
||||||
val mem: Seq[TLInwardNode]
|
val mem: Seq[TLInwardNode]
|
||||||
|
|
||||||
val coreplex = LazyModule(new DefaultCoreplex)
|
val coreplex = LazyModule(new DefaultCoreplex)
|
||||||
|
|
||||||
coreplex.l2in := l2.node
|
coreplex.l2in :=* l2.node
|
||||||
socBus.node := coreplex.mmio
|
socBus.node := coreplex.mmio
|
||||||
coreplex.mmioInt := intBus.intnode
|
coreplex.mmioInt := intBus.intnode
|
||||||
mem.foreach { _ := coreplex.mem }
|
mem.foreach { _ := coreplex.mem }
|
||||||
}
|
}
|
||||||
|
|
||||||
trait RocketPlexMasterBundle extends L2CrossbarBundle {
|
trait RocketPlexMasterBundle extends TopNetworkBundle {
|
||||||
val outer: RocketPlexMaster
|
val outer: RocketPlexMaster
|
||||||
}
|
}
|
||||||
|
|
||||||
trait RocketPlexMasterModule extends L2CrossbarModule {
|
trait RocketPlexMasterModule extends TopNetworkModule {
|
||||||
val outer: RocketPlexMaster
|
val outer: RocketPlexMaster
|
||||||
val io: RocketPlexMasterBundle
|
val io: RocketPlexMasterBundle
|
||||||
val clock: Clock
|
val clock: Clock
|
||||||
|
@ -19,10 +19,6 @@ object AHBImp extends NodeImp[AHBMasterPortParameters, AHBSlavePortParameters, A
|
|||||||
override def labelI(ei: AHBEdgeParameters) = (ei.slave.beatBytes * 8).toString
|
override def labelI(ei: AHBEdgeParameters) = (ei.slave.beatBytes * 8).toString
|
||||||
override def labelO(eo: AHBEdgeParameters) = (eo.slave.beatBytes * 8).toString
|
override def labelO(eo: AHBEdgeParameters) = (eo.slave.beatBytes * 8).toString
|
||||||
|
|
||||||
def connect(bo: => AHBBundle, bi: => AHBBundle, ei: => AHBEdgeParameters)(implicit p: Parameters, sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = {
|
|
||||||
(None, () => { bi <> bo })
|
|
||||||
}
|
|
||||||
|
|
||||||
override def mixO(pd: AHBMasterPortParameters, node: OutwardNode[AHBMasterPortParameters, AHBSlavePortParameters, AHBBundle]): AHBMasterPortParameters =
|
override def mixO(pd: AHBMasterPortParameters, node: OutwardNode[AHBMasterPortParameters, AHBSlavePortParameters, AHBBundle]): AHBMasterPortParameters =
|
||||||
pd.copy(masters = pd.masters.map { c => c.copy (nodePath = node +: c.nodePath) })
|
pd.copy(masters = pd.masters.map { c => c.copy (nodePath = node +: c.nodePath) })
|
||||||
override def mixI(pu: AHBSlavePortParameters, node: InwardNode[AHBMasterPortParameters, AHBSlavePortParameters, AHBBundle]): AHBSlavePortParameters =
|
override def mixI(pu: AHBSlavePortParameters, node: InwardNode[AHBMasterPortParameters, AHBSlavePortParameters, AHBBundle]): AHBSlavePortParameters =
|
||||||
@ -31,16 +27,14 @@ object AHBImp extends NodeImp[AHBMasterPortParameters, AHBSlavePortParameters, A
|
|||||||
|
|
||||||
// Nodes implemented inside modules
|
// Nodes implemented inside modules
|
||||||
case class AHBIdentityNode() extends IdentityNode(AHBImp)
|
case class AHBIdentityNode() extends IdentityNode(AHBImp)
|
||||||
case class AHBMasterNode(portParams: AHBMasterPortParameters, numPorts: Range.Inclusive = 1 to 1)
|
case class AHBMasterNode(portParams: Seq[AHBMasterPortParameters]) extends SourceNode(AHBImp)(portParams)
|
||||||
extends SourceNode(AHBImp)(portParams, numPorts)
|
case class AHBSlaveNode(portParams: Seq[AHBSlavePortParameters]) extends SinkNode(AHBImp)(portParams)
|
||||||
case class AHBSlaveNode(portParams: AHBSlavePortParameters, numPorts: Range.Inclusive = 1 to 1)
|
case class AHBNexusNode(
|
||||||
extends SinkNode(AHBImp)(portParams, numPorts)
|
masterFn: Seq[AHBMasterPortParameters] => AHBMasterPortParameters,
|
||||||
case class AHBAdapterNode(
|
slaveFn: Seq[AHBSlavePortParameters] => AHBSlavePortParameters,
|
||||||
masterFn: Seq[AHBMasterPortParameters] => AHBMasterPortParameters,
|
numMasterPorts: Range.Inclusive = 1 to 999,
|
||||||
slaveFn: Seq[AHBSlavePortParameters] => AHBSlavePortParameters,
|
numSlavePorts: Range.Inclusive = 1 to 999)
|
||||||
numMasterPorts: Range.Inclusive = 1 to 1,
|
extends NexusNode(AHBImp)(masterFn, slaveFn, numMasterPorts, numSlavePorts)
|
||||||
numSlavePorts: Range.Inclusive = 1 to 1)
|
|
||||||
extends InteriorNode(AHBImp)(masterFn, slaveFn, numMasterPorts, numSlavePorts)
|
|
||||||
|
|
||||||
// Nodes passed from an inner module
|
// Nodes passed from an inner module
|
||||||
case class AHBOutputNode() extends OutputNode(AHBImp)
|
case class AHBOutputNode() extends OutputNode(AHBImp)
|
||||||
|
@ -9,13 +9,13 @@ import regmapper._
|
|||||||
import scala.math.{min,max}
|
import scala.math.{min,max}
|
||||||
|
|
||||||
class AHBRegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false)
|
class AHBRegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false)
|
||||||
extends AHBSlaveNode(AHBSlavePortParameters(
|
extends AHBSlaveNode(Seq(AHBSlavePortParameters(
|
||||||
Seq(AHBSlaveParameters(
|
Seq(AHBSlaveParameters(
|
||||||
address = Seq(address),
|
address = Seq(address),
|
||||||
executable = executable,
|
executable = executable,
|
||||||
supportsWrite = TransferSizes(1, min(address.alignment.toInt, beatBytes * AHBParameters.maxTransfer)),
|
supportsWrite = TransferSizes(1, min(address.alignment.toInt, beatBytes * AHBParameters.maxTransfer)),
|
||||||
supportsRead = TransferSizes(1, min(address.alignment.toInt, beatBytes * AHBParameters.maxTransfer)))),
|
supportsRead = TransferSizes(1, min(address.alignment.toInt, beatBytes * AHBParameters.maxTransfer)))),
|
||||||
beatBytes = beatBytes))
|
beatBytes = beatBytes)))
|
||||||
{
|
{
|
||||||
require (address.contiguous)
|
require (address.contiguous)
|
||||||
|
|
||||||
|
@ -8,14 +8,14 @@ import diplomacy._
|
|||||||
|
|
||||||
class AHBRAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule
|
class AHBRAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule
|
||||||
{
|
{
|
||||||
val node = AHBSlaveNode(AHBSlavePortParameters(
|
val node = AHBSlaveNode(Seq(AHBSlavePortParameters(
|
||||||
Seq(AHBSlaveParameters(
|
Seq(AHBSlaveParameters(
|
||||||
address = List(address),
|
address = List(address),
|
||||||
regionType = RegionType.UNCACHED,
|
regionType = RegionType.UNCACHED,
|
||||||
executable = executable,
|
executable = executable,
|
||||||
supportsRead = TransferSizes(1, beatBytes * AHBParameters.maxTransfer),
|
supportsRead = TransferSizes(1, beatBytes * AHBParameters.maxTransfer),
|
||||||
supportsWrite = TransferSizes(1, beatBytes * AHBParameters.maxTransfer))),
|
supportsWrite = TransferSizes(1, beatBytes * AHBParameters.maxTransfer))),
|
||||||
beatBytes = beatBytes))
|
beatBytes = beatBytes)))
|
||||||
|
|
||||||
// We require the address range to include an entire beat (for the write mask)
|
// We require the address range to include an entire beat (for the write mask)
|
||||||
require ((address.mask & (beatBytes-1)) == beatBytes-1)
|
require ((address.mask & (beatBytes-1)) == beatBytes-1)
|
||||||
|
@ -9,7 +9,7 @@ import regmapper._
|
|||||||
import scala.math.{min,max}
|
import scala.math.{min,max}
|
||||||
|
|
||||||
class AHBFanout()(implicit p: Parameters) extends LazyModule {
|
class AHBFanout()(implicit p: Parameters) extends LazyModule {
|
||||||
val node = AHBAdapterNode(
|
val node = AHBNexusNode(
|
||||||
numSlavePorts = 1 to 1,
|
numSlavePorts = 1 to 1,
|
||||||
numMasterPorts = 1 to 32,
|
numMasterPorts = 1 to 32,
|
||||||
masterFn = { case Seq(m) => m },
|
masterFn = { case Seq(m) => m },
|
||||||
|
@ -19,10 +19,6 @@ object APBImp extends NodeImp[APBMasterPortParameters, APBSlavePortParameters, A
|
|||||||
override def labelI(ei: APBEdgeParameters) = (ei.slave.beatBytes * 8).toString
|
override def labelI(ei: APBEdgeParameters) = (ei.slave.beatBytes * 8).toString
|
||||||
override def labelO(eo: APBEdgeParameters) = (eo.slave.beatBytes * 8).toString
|
override def labelO(eo: APBEdgeParameters) = (eo.slave.beatBytes * 8).toString
|
||||||
|
|
||||||
def connect(bo: => APBBundle, bi: => APBBundle, ei: => APBEdgeParameters)(implicit p: Parameters, sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = {
|
|
||||||
(None, () => { bi <> bo })
|
|
||||||
}
|
|
||||||
|
|
||||||
override def mixO(pd: APBMasterPortParameters, node: OutwardNode[APBMasterPortParameters, APBSlavePortParameters, APBBundle]): APBMasterPortParameters =
|
override def mixO(pd: APBMasterPortParameters, node: OutwardNode[APBMasterPortParameters, APBSlavePortParameters, APBBundle]): APBMasterPortParameters =
|
||||||
pd.copy(masters = pd.masters.map { c => c.copy (nodePath = node +: c.nodePath) })
|
pd.copy(masters = pd.masters.map { c => c.copy (nodePath = node +: c.nodePath) })
|
||||||
override def mixI(pu: APBSlavePortParameters, node: InwardNode[APBMasterPortParameters, APBSlavePortParameters, APBBundle]): APBSlavePortParameters =
|
override def mixI(pu: APBSlavePortParameters, node: InwardNode[APBMasterPortParameters, APBSlavePortParameters, APBBundle]): APBSlavePortParameters =
|
||||||
@ -31,16 +27,14 @@ object APBImp extends NodeImp[APBMasterPortParameters, APBSlavePortParameters, A
|
|||||||
|
|
||||||
// Nodes implemented inside modules
|
// Nodes implemented inside modules
|
||||||
case class APBIdentityNode() extends IdentityNode(APBImp)
|
case class APBIdentityNode() extends IdentityNode(APBImp)
|
||||||
case class APBMasterNode(portParams: APBMasterPortParameters, numPorts: Range.Inclusive = 1 to 1)
|
case class APBMasterNode(portParams: Seq[APBMasterPortParameters]) extends SourceNode(APBImp)(portParams)
|
||||||
extends SourceNode(APBImp)(portParams, numPorts)
|
case class APBSlaveNode(portParams: Seq[APBSlavePortParameters]) extends SinkNode(APBImp)(portParams)
|
||||||
case class APBSlaveNode(portParams: APBSlavePortParameters, numPorts: Range.Inclusive = 1 to 1)
|
case class APBNexusNode(
|
||||||
extends SinkNode(APBImp)(portParams, numPorts)
|
masterFn: Seq[APBMasterPortParameters] => APBMasterPortParameters,
|
||||||
case class APBAdapterNode(
|
slaveFn: Seq[APBSlavePortParameters] => APBSlavePortParameters,
|
||||||
masterFn: Seq[APBMasterPortParameters] => APBMasterPortParameters,
|
|
||||||
slaveFn: Seq[APBSlavePortParameters] => APBSlavePortParameters,
|
|
||||||
numMasterPorts: Range.Inclusive = 1 to 1,
|
numMasterPorts: Range.Inclusive = 1 to 1,
|
||||||
numSlavePorts: Range.Inclusive = 1 to 1)
|
numSlavePorts: Range.Inclusive = 1 to 1)
|
||||||
extends InteriorNode(APBImp)(masterFn, slaveFn, numMasterPorts, numSlavePorts)
|
extends NexusNode(APBImp)(masterFn, slaveFn, numMasterPorts, numSlavePorts)
|
||||||
|
|
||||||
// Nodes passed from an inner module
|
// Nodes passed from an inner module
|
||||||
case class APBOutputNode() extends OutputNode(APBImp)
|
case class APBOutputNode() extends OutputNode(APBImp)
|
||||||
|
@ -9,13 +9,13 @@ import regmapper._
|
|||||||
import scala.math.{min,max}
|
import scala.math.{min,max}
|
||||||
|
|
||||||
class APBRegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false)
|
class APBRegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false)
|
||||||
extends APBSlaveNode(APBSlavePortParameters(
|
extends APBSlaveNode(Seq(APBSlavePortParameters(
|
||||||
Seq(APBSlaveParameters(
|
Seq(APBSlaveParameters(
|
||||||
address = Seq(address),
|
address = Seq(address),
|
||||||
executable = executable,
|
executable = executable,
|
||||||
supportsWrite = true,
|
supportsWrite = true,
|
||||||
supportsRead = true)),
|
supportsRead = true)),
|
||||||
beatBytes = beatBytes))
|
beatBytes = beatBytes)))
|
||||||
{
|
{
|
||||||
require (address.contiguous)
|
require (address.contiguous)
|
||||||
|
|
||||||
|
@ -8,14 +8,14 @@ import diplomacy._
|
|||||||
|
|
||||||
class APBRAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule
|
class APBRAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule
|
||||||
{
|
{
|
||||||
val node = APBSlaveNode(APBSlavePortParameters(
|
val node = APBSlaveNode(Seq(APBSlavePortParameters(
|
||||||
Seq(APBSlaveParameters(
|
Seq(APBSlaveParameters(
|
||||||
address = List(address),
|
address = List(address),
|
||||||
regionType = RegionType.UNCACHED,
|
regionType = RegionType.UNCACHED,
|
||||||
executable = executable,
|
executable = executable,
|
||||||
supportsRead = true,
|
supportsRead = true,
|
||||||
supportsWrite = true)),
|
supportsWrite = true)),
|
||||||
beatBytes = beatBytes))
|
beatBytes = beatBytes)))
|
||||||
|
|
||||||
// We require the address range to include an entire beat (for the write mask)
|
// We require the address range to include an entire beat (for the write mask)
|
||||||
require ((address.mask & (beatBytes-1)) == beatBytes-1)
|
require ((address.mask & (beatBytes-1)) == beatBytes-1)
|
||||||
|
@ -9,7 +9,7 @@ import regmapper._
|
|||||||
import scala.math.{min,max}
|
import scala.math.{min,max}
|
||||||
|
|
||||||
class APBFanout()(implicit p: Parameters) extends LazyModule {
|
class APBFanout()(implicit p: Parameters) extends LazyModule {
|
||||||
val node = APBAdapterNode(
|
val node = APBNexusNode(
|
||||||
numSlavePorts = 1 to 1,
|
numSlavePorts = 1 to 1,
|
||||||
numMasterPorts = 1 to 32,
|
numMasterPorts = 1 to 32,
|
||||||
masterFn = { case Seq(m) => m },
|
masterFn = { case Seq(m) => m },
|
||||||
|
@ -18,8 +18,8 @@ class AXI4Buffer(aw: Int = 2, w: Int = 2, b: Int = 2, ar: Int = 2, r: Int = 2, p
|
|||||||
require (r >= 0)
|
require (r >= 0)
|
||||||
|
|
||||||
val node = AXI4AdapterNode(
|
val node = AXI4AdapterNode(
|
||||||
masterFn = { case Seq(p) => p },
|
masterFn = { p => p },
|
||||||
slaveFn = { case Seq(p) => p.copy(minLatency = p.minLatency + min(1,min(aw,ar)) + min(1,min(r,b))) })
|
slaveFn = { p => p.copy(minLatency = p.minLatency + min(1,min(aw,ar)) + min(1,min(r,b))) })
|
||||||
|
|
||||||
lazy val module = new LazyModuleImp(this) {
|
lazy val module = new LazyModuleImp(this) {
|
||||||
val io = new Bundle {
|
val io = new Bundle {
|
||||||
|
@ -23,8 +23,8 @@ class AXI4Fragmenter(lite: Boolean = false, maxInFlight: => Int = 32, combinatio
|
|||||||
def mapMaster(m: AXI4MasterParameters) = m.copy(aligned = true)
|
def mapMaster(m: AXI4MasterParameters) = m.copy(aligned = true)
|
||||||
|
|
||||||
val node = AXI4AdapterNode(
|
val node = AXI4AdapterNode(
|
||||||
masterFn = { case Seq(mp) => mp.copy(masters = mp.masters.map(m => mapMaster(m))) },
|
masterFn = { mp => mp.copy(masters = mp.masters.map(m => mapMaster(m))) },
|
||||||
slaveFn = { case Seq(sp) => sp.copy(slaves = sp.slaves .map(s => mapSlave(s, sp.beatBytes))) })
|
slaveFn = { sp => sp.copy(slaves = sp.slaves .map(s => mapSlave(s, sp.beatBytes))) })
|
||||||
|
|
||||||
lazy val module = new LazyModuleImp(this) {
|
lazy val module = new LazyModuleImp(this) {
|
||||||
val io = new Bundle {
|
val io = new Bundle {
|
||||||
@ -32,256 +32,253 @@ class AXI4Fragmenter(lite: Boolean = false, maxInFlight: => Int = 32, combinatio
|
|||||||
val out = node.bundleOut
|
val out = node.bundleOut
|
||||||
}
|
}
|
||||||
|
|
||||||
val edgeOut = node.edgesOut(0)
|
((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
|
||||||
val edgeIn = node.edgesIn(0)
|
val slave = edgeOut.slave
|
||||||
val slave = edgeOut.slave
|
val slaves = slave.slaves
|
||||||
val slaves = slave.slaves
|
val beatBytes = slave.beatBytes
|
||||||
val beatBytes = slave.beatBytes
|
val lgBytes = log2Ceil(beatBytes)
|
||||||
val lgBytes = log2Ceil(beatBytes)
|
val master = edgeIn.master
|
||||||
val master = edgeIn.master
|
val masters = master.masters
|
||||||
val masters = master.masters
|
|
||||||
|
|
||||||
// If the user claimed this was a lite interface, then there must be only one Id
|
// If the user claimed this was a lite interface, then there must be only one Id
|
||||||
require (!lite || master.endId == 1)
|
require (!lite || master.endId == 1)
|
||||||
|
|
||||||
// We don't support fragmenting to sub-beat accesses
|
// We don't support fragmenting to sub-beat accesses
|
||||||
slaves.foreach { s =>
|
slaves.foreach { s =>
|
||||||
require (!s.supportsRead || s.supportsRead.contains(beatBytes))
|
require (!s.supportsRead || s.supportsRead.contains(beatBytes))
|
||||||
require (!s.supportsWrite || s.supportsWrite.contains(beatBytes))
|
require (!s.supportsWrite || s.supportsWrite.contains(beatBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We need to decompose a request into
|
/* We need to decompose a request into
|
||||||
* FIXED => each beat is a new request
|
* FIXED => each beat is a new request
|
||||||
* WRAP/INCR => take xfr up to next power of two, capped by max size of target
|
* WRAP/INCR => take xfr up to next power of two, capped by max size of target
|
||||||
*
|
*
|
||||||
* On AR and AW, we fragment one request into many
|
* On AR and AW, we fragment one request into many
|
||||||
* On W we set 'last' on beats which are fragment boundaries
|
* On W we set 'last' on beats which are fragment boundaries
|
||||||
* On R we clear 'last' on the fragments being reassembled
|
* On R we clear 'last' on the fragments being reassembled
|
||||||
* On B we clear 'valid' on the responses for the injected fragments
|
* On B we clear 'valid' on the responses for the injected fragments
|
||||||
*
|
*
|
||||||
* AR=>R and AW+W=>B are completely independent state machines.
|
* AR=>R and AW+W=>B are completely independent state machines.
|
||||||
*/
|
|
||||||
|
|
||||||
/* Returns the number of beats to execute and the new address */
|
|
||||||
def fragment(a: IrrevocableIO[AXI4BundleA], supportedSizes1: Seq[Int]): (IrrevocableIO[AXI4BundleA], Bool, UInt) = {
|
|
||||||
val out = Wire(a)
|
|
||||||
|
|
||||||
val busy = RegInit(Bool(false))
|
|
||||||
val r_addr = Reg(UInt(width = a.bits.params.addrBits))
|
|
||||||
val r_len = Reg(UInt(width = AXI4Parameters.lenBits))
|
|
||||||
|
|
||||||
val len = Mux(busy, r_len, a.bits.len)
|
|
||||||
val addr = Mux(busy, r_addr, a.bits.addr)
|
|
||||||
|
|
||||||
val lo = if (lgBytes == 0) UInt(0) else addr(lgBytes-1, 0)
|
|
||||||
val hi = addr >> lgBytes
|
|
||||||
val alignment = hi(AXI4Parameters.lenBits-1,0)
|
|
||||||
|
|
||||||
val allSame = supportedSizes1.filter(_ >= 0).distinct.size <= 1
|
|
||||||
val dynamic1 = Mux1H(slave.findFast(addr), supportedSizes1.map(s => UInt(max(0, s))))
|
|
||||||
val fixed1 = UInt(supportedSizes1.filter(_ >= 0).headOption.getOrElse(0))
|
|
||||||
|
|
||||||
/* We need to compute the largest transfer allowed by the AXI len.
|
|
||||||
* len+1 is the number of beats to execute.
|
|
||||||
* We want the MSB(len+1)-1; one less than the largest power of two we could execute.
|
|
||||||
* There are two cases; either len is 2^n-1 in which case we leave it unchanged, ELSE
|
|
||||||
* fill the bits from highest to lowest, and shift right by one bit.
|
|
||||||
*/
|
*/
|
||||||
val fillLow = rightOR(len) >> 1 // set all bits in positions < a set bit
|
|
||||||
val wipeHigh = ~leftOR(~len) // clear all bits in position >= a cleared bit
|
|
||||||
val remain1 = fillLow | wipeHigh // MSB(a.len+1)-1
|
|
||||||
val align1 = ~leftOR(alignment) // transfer size limited by address alignment
|
|
||||||
val support1 = if (allSame) fixed1 else dynamic1 // maximum supported size-1 based on target address
|
|
||||||
val maxSupported1 = remain1 & align1 & support1 // Take the minimum of all the limits
|
|
||||||
|
|
||||||
// Things that cause us to degenerate to a single beat
|
/* Returns the number of beats to execute and the new address */
|
||||||
val fixed = a.bits.burst === AXI4Parameters.BURST_FIXED
|
def fragment(a: IrrevocableIO[AXI4BundleA], supportedSizes1: Seq[Int]): (IrrevocableIO[AXI4BundleA], Bool, UInt) = {
|
||||||
val narrow = a.bits.size =/= UInt(lgBytes)
|
val out = Wire(a)
|
||||||
val bad = fixed || narrow
|
|
||||||
|
|
||||||
// The number of beats-1 to execute
|
val busy = RegInit(Bool(false))
|
||||||
val beats1 = Mux(bad, UInt(0), maxSupported1)
|
val r_addr = Reg(UInt(width = a.bits.params.addrBits))
|
||||||
val beats = OH1ToOH(beats1) // beats1 + 1
|
val r_len = Reg(UInt(width = AXI4Parameters.lenBits))
|
||||||
|
|
||||||
val inc_addr = addr + (beats << a.bits.size) // address after adding transfer
|
val len = Mux(busy, r_len, a.bits.len)
|
||||||
val wrapMask = a.bits.bytes1() // only these bits may change, if wrapping
|
val addr = Mux(busy, r_addr, a.bits.addr)
|
||||||
val mux_addr = Wire(init = inc_addr)
|
|
||||||
when (a.bits.burst === AXI4Parameters.BURST_WRAP) {
|
val lo = if (lgBytes == 0) UInt(0) else addr(lgBytes-1, 0)
|
||||||
mux_addr := (inc_addr & wrapMask) | ~(~a.bits.addr | wrapMask)
|
val hi = addr >> lgBytes
|
||||||
}
|
val alignment = hi(AXI4Parameters.lenBits-1,0)
|
||||||
when (a.bits.burst === AXI4Parameters.BURST_FIXED) {
|
|
||||||
mux_addr := a.bits.addr
|
val allSame = supportedSizes1.filter(_ >= 0).distinct.size <= 1
|
||||||
|
val dynamic1 = Mux1H(slave.findFast(addr), supportedSizes1.map(s => UInt(max(0, s))))
|
||||||
|
val fixed1 = UInt(supportedSizes1.filter(_ >= 0).headOption.getOrElse(0))
|
||||||
|
|
||||||
|
/* We need to compute the largest transfer allowed by the AXI len.
|
||||||
|
* len+1 is the number of beats to execute.
|
||||||
|
* We want the MSB(len+1)-1; one less than the largest power of two we could execute.
|
||||||
|
* There are two cases; either len is 2^n-1 in which case we leave it unchanged, ELSE
|
||||||
|
* fill the bits from highest to lowest, and shift right by one bit.
|
||||||
|
*/
|
||||||
|
val fillLow = rightOR(len) >> 1 // set all bits in positions < a set bit
|
||||||
|
val wipeHigh = ~leftOR(~len) // clear all bits in position >= a cleared bit
|
||||||
|
val remain1 = fillLow | wipeHigh // MSB(a.len+1)-1
|
||||||
|
val align1 = ~leftOR(alignment) // transfer size limited by address alignment
|
||||||
|
val support1 = if (allSame) fixed1 else dynamic1 // maximum supported size-1 based on target address
|
||||||
|
val maxSupported1 = remain1 & align1 & support1 // Take the minimum of all the limits
|
||||||
|
|
||||||
|
// Things that cause us to degenerate to a single beat
|
||||||
|
val fixed = a.bits.burst === AXI4Parameters.BURST_FIXED
|
||||||
|
val narrow = a.bits.size =/= UInt(lgBytes)
|
||||||
|
val bad = fixed || narrow
|
||||||
|
|
||||||
|
// The number of beats-1 to execute
|
||||||
|
val beats1 = Mux(bad, UInt(0), maxSupported1)
|
||||||
|
val beats = OH1ToOH(beats1) // beats1 + 1
|
||||||
|
|
||||||
|
val inc_addr = addr + (beats << a.bits.size) // address after adding transfer
|
||||||
|
val wrapMask = a.bits.bytes1() // only these bits may change, if wrapping
|
||||||
|
val mux_addr = Wire(init = inc_addr)
|
||||||
|
when (a.bits.burst === AXI4Parameters.BURST_WRAP) {
|
||||||
|
mux_addr := (inc_addr & wrapMask) | ~(~a.bits.addr | wrapMask)
|
||||||
|
}
|
||||||
|
when (a.bits.burst === AXI4Parameters.BURST_FIXED) {
|
||||||
|
mux_addr := a.bits.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
val last = beats1 === len
|
||||||
|
a.ready := out.ready && last
|
||||||
|
out.valid := a.valid
|
||||||
|
|
||||||
|
out.bits := a.bits
|
||||||
|
out.bits.len := beats1
|
||||||
|
|
||||||
|
// We forcibly align every access. If the first beat was misaligned, the strb bits
|
||||||
|
// for the lower addresses must not have been set. Therefore, rounding the address
|
||||||
|
// down is harmless. We can do this after the address update algorithm, because the
|
||||||
|
// incremented values will be rounded down the same way. Furthermore, a subword
|
||||||
|
// offset cannot cause a premature wrap-around.
|
||||||
|
out.bits.addr := ~(~addr | UIntToOH1(a.bits.size, lgBytes))
|
||||||
|
|
||||||
|
when (out.fire()) {
|
||||||
|
busy := !last
|
||||||
|
r_addr := mux_addr
|
||||||
|
r_len := len - beats
|
||||||
|
}
|
||||||
|
|
||||||
|
(out, last, beats)
|
||||||
}
|
}
|
||||||
|
|
||||||
val last = beats1 === len
|
// The size to which we will fragment the access
|
||||||
a.ready := out.ready && last
|
val readSizes1 = slaves.map(s => s.supportsRead .max/beatBytes-1)
|
||||||
out.valid := a.valid
|
val writeSizes1 = slaves.map(s => s.supportsWrite.max/beatBytes-1)
|
||||||
|
|
||||||
out.bits := a.bits
|
// Indirection variables for inputs and outputs; makes transformation application easier
|
||||||
out.bits.len := beats1
|
val (in_ar, ar_last, _) = fragment(Queue.irrevocable(in.ar, 1, flow=true), readSizes1)
|
||||||
|
val (in_aw, aw_last, w_beats) = fragment(Queue.irrevocable(in.aw, 1, flow=true), writeSizes1)
|
||||||
|
val in_w = in.w
|
||||||
|
val in_r = in.r
|
||||||
|
val in_b = in.b
|
||||||
|
val out_ar = Wire(out.ar)
|
||||||
|
val out_aw = out.aw
|
||||||
|
val out_w = out.w
|
||||||
|
val out_r = Wire(out.r)
|
||||||
|
val out_b = Wire(out.b)
|
||||||
|
|
||||||
// We forcibly align every access. If the first beat was misaligned, the strb bits
|
val depth = if (combinational) 1 else 2
|
||||||
// for the lower addresses must not have been set. Therefore, rounding the address
|
// In case a slave ties arready := rready, we need a queue to break the combinational loop
|
||||||
// down is harmless. We can do this after the address update algorithm, because the
|
// between the two branches (in_ar => {out_ar => out_r, sideband} => in_r).
|
||||||
// incremented values will be rounded down the same way. Furthermore, a subword
|
if (in.ar.bits.getWidth < in.r.bits.getWidth) {
|
||||||
// offset cannot cause a premature wrap-around.
|
out.ar <> Queue(out_ar, depth, flow=combinational)
|
||||||
out.bits.addr := ~(~addr | UIntToOH1(a.bits.size, lgBytes))
|
out_r <> out.r
|
||||||
|
} else {
|
||||||
when (out.fire()) {
|
out.ar <> out_ar
|
||||||
busy := !last
|
out_r <> Queue(out.r, depth, flow=combinational)
|
||||||
r_addr := mux_addr
|
|
||||||
r_len := len - beats
|
|
||||||
}
|
}
|
||||||
|
// In case a slave ties awready := bready or wready := bready, we need this queue
|
||||||
|
out_b <> Queue(out.b, depth, flow=combinational)
|
||||||
|
|
||||||
(out, last, beats)
|
// Sideband to track which transfers were the last fragment
|
||||||
}
|
def sideband() = if (lite) {
|
||||||
|
Module(new Queue(Bool(), maxInFlight, flow=combinational)).io
|
||||||
|
} else {
|
||||||
|
Module(new AXI4FragmenterSideband(maxInFlight, flow=combinational)).io
|
||||||
|
}
|
||||||
|
val sideband_ar_r = sideband()
|
||||||
|
val sideband_aw_b = sideband()
|
||||||
|
|
||||||
val in = io.in(0)
|
// AR flow control
|
||||||
val out = io.out(0)
|
out_ar.valid := in_ar.valid && sideband_ar_r.enq.ready
|
||||||
|
in_ar.ready := sideband_ar_r.enq.ready && out_ar.ready
|
||||||
|
sideband_ar_r.enq.valid := in_ar.valid && out_ar.ready
|
||||||
|
out_ar.bits := in_ar.bits
|
||||||
|
sideband_ar_r.enq.bits := ar_last
|
||||||
|
|
||||||
// The size to which we will fragment the access
|
// When does W channel start counting a new transfer
|
||||||
val readSizes1 = slaves.map(s => s.supportsRead .max/beatBytes-1)
|
val wbeats_latched = RegInit(Bool(false))
|
||||||
val writeSizes1 = slaves.map(s => s.supportsWrite.max/beatBytes-1)
|
val wbeats_ready = Wire(Bool())
|
||||||
|
val wbeats_valid = Wire(Bool())
|
||||||
|
when (wbeats_valid && wbeats_ready) { wbeats_latched := Bool(true) }
|
||||||
|
when (out_aw.fire()) { wbeats_latched := Bool(false) }
|
||||||
|
|
||||||
// Indirection variables for inputs and outputs; makes transformation application easier
|
// AW flow control
|
||||||
val (in_ar, ar_last, _) = fragment(Queue.irrevocable(in.ar, 1, flow=true), readSizes1)
|
out_aw.valid := in_aw.valid && sideband_aw_b.enq.ready && (wbeats_ready || wbeats_latched)
|
||||||
val (in_aw, aw_last, w_beats) = fragment(Queue.irrevocable(in.aw, 1, flow=true), writeSizes1)
|
in_aw.ready := sideband_aw_b.enq.ready && out_aw.ready && (wbeats_ready || wbeats_latched)
|
||||||
val in_w = in.w
|
sideband_aw_b.enq.valid := in_aw.valid && out_aw.ready && (wbeats_ready || wbeats_latched)
|
||||||
val in_r = in.r
|
wbeats_valid := in_aw.valid && !wbeats_latched
|
||||||
val in_b = in.b
|
out_aw.bits := in_aw.bits
|
||||||
val out_ar = Wire(out.ar)
|
sideband_aw_b.enq.bits := aw_last
|
||||||
val out_aw = out.aw
|
|
||||||
val out_w = out.w
|
|
||||||
val out_r = Wire(out.r)
|
|
||||||
val out_b = Wire(out.b)
|
|
||||||
|
|
||||||
val depth = if (combinational) 1 else 2
|
// We need to inject 'last' into the W channel fragments, count!
|
||||||
// In case a slave ties arready := rready, we need a queue to break the combinational loop
|
val w_counter = RegInit(UInt(0, width = AXI4Parameters.lenBits+1))
|
||||||
// between the two branches (in_ar => {out_ar => out_r, sideband} => in_r).
|
val w_idle = w_counter === UInt(0)
|
||||||
if (in.ar.bits.getWidth < in.r.bits.getWidth) {
|
val w_todo = Mux(w_idle, Mux(wbeats_valid, w_beats, UInt(0)), w_counter)
|
||||||
out.ar <> Queue(out_ar, depth, flow=combinational)
|
val w_last = w_todo === UInt(1)
|
||||||
out_r <> out.r
|
w_counter := w_todo - out_w.fire()
|
||||||
} else {
|
assert (!out_w.fire() || w_todo =/= UInt(0)) // underflow impossible
|
||||||
out.ar <> out_ar
|
|
||||||
out_r <> Queue(out.r, depth, flow=combinational)
|
|
||||||
}
|
|
||||||
// In case a slave ties awready := bready or wready := bready, we need this queue
|
|
||||||
out_b <> Queue(out.b, depth, flow=combinational)
|
|
||||||
|
|
||||||
// Sideband to track which transfers were the last fragment
|
// W flow control
|
||||||
def sideband() = if (lite) {
|
wbeats_ready := w_idle
|
||||||
Module(new Queue(Bool(), maxInFlight, flow=combinational)).io
|
out_w.valid := in_w.valid && (!wbeats_ready || wbeats_valid)
|
||||||
} else {
|
in_w.ready := out_w.ready && (!wbeats_ready || wbeats_valid)
|
||||||
Module(new AXI4FragmenterSideband(maxInFlight, flow=combinational)).io
|
out_w.bits := in_w.bits
|
||||||
}
|
out_w.bits.last := w_last
|
||||||
val sideband_ar_r = sideband()
|
// We should also recreate the last last
|
||||||
val sideband_aw_b = sideband()
|
assert (!out_w.valid || !in_w.bits.last || w_last)
|
||||||
|
|
||||||
// AR flow control
|
// R flow control
|
||||||
out_ar.valid := in_ar.valid && sideband_ar_r.enq.ready
|
val r_last = out_r.bits.last
|
||||||
in_ar.ready := sideband_ar_r.enq.ready && out_ar.ready
|
in_r.valid := out_r.valid && (!r_last || sideband_ar_r.deq.valid)
|
||||||
sideband_ar_r.enq.valid := in_ar.valid && out_ar.ready
|
out_r.ready := in_r.ready && (!r_last || sideband_ar_r.deq.valid)
|
||||||
out_ar.bits := in_ar.bits
|
sideband_ar_r.deq.ready := r_last && out_r.valid && in_r.ready
|
||||||
sideband_ar_r.enq.bits := ar_last
|
in_r.bits := out_r.bits
|
||||||
|
in_r.bits.last := r_last && sideband_ar_r.deq.bits
|
||||||
|
|
||||||
// When does W channel start counting a new transfer
|
// B flow control
|
||||||
val wbeats_latched = RegInit(Bool(false))
|
val b_last = sideband_aw_b.deq.bits
|
||||||
val wbeats_ready = Wire(Bool())
|
in_b.valid := out_b.valid && sideband_aw_b.deq.valid && b_last
|
||||||
val wbeats_valid = Wire(Bool())
|
out_b.ready := sideband_aw_b.deq.valid && (!b_last || in_b.ready)
|
||||||
when (wbeats_valid && wbeats_ready) { wbeats_latched := Bool(true) }
|
sideband_aw_b.deq.ready := out_b.valid && (!b_last || in_b.ready)
|
||||||
when (out_aw.fire()) { wbeats_latched := Bool(false) }
|
in_b.bits := out_b.bits
|
||||||
|
|
||||||
// AW flow control
|
// Merge errors from dropped B responses
|
||||||
out_aw.valid := in_aw.valid && sideband_aw_b.enq.ready && (wbeats_ready || wbeats_latched)
|
val r_resp = RegInit(UInt(0, width = AXI4Parameters.respBits))
|
||||||
in_aw.ready := sideband_aw_b.enq.ready && out_aw.ready && (wbeats_ready || wbeats_latched)
|
val resp = out_b.bits.resp | r_resp
|
||||||
sideband_aw_b.enq.valid := in_aw.valid && out_aw.ready && (wbeats_ready || wbeats_latched)
|
when (out_b.fire()) { r_resp := Mux(b_last, UInt(0), resp) }
|
||||||
wbeats_valid := in_aw.valid && !wbeats_latched
|
in_b.bits.resp := resp
|
||||||
out_aw.bits := in_aw.bits
|
|
||||||
sideband_aw_b.enq.bits := aw_last
|
|
||||||
|
|
||||||
// We need to inject 'last' into the W channel fragments, count!
|
|
||||||
val w_counter = RegInit(UInt(0, width = AXI4Parameters.lenBits+1))
|
|
||||||
val w_idle = w_counter === UInt(0)
|
|
||||||
val w_todo = Mux(w_idle, Mux(wbeats_valid, w_beats, UInt(0)), w_counter)
|
|
||||||
val w_last = w_todo === UInt(1)
|
|
||||||
w_counter := w_todo - out_w.fire()
|
|
||||||
assert (!out_w.fire() || w_todo =/= UInt(0)) // underflow impossible
|
|
||||||
|
|
||||||
// W flow control
|
|
||||||
wbeats_ready := w_idle
|
|
||||||
out_w.valid := in_w.valid && (!wbeats_ready || wbeats_valid)
|
|
||||||
in_w.ready := out_w.ready && (!wbeats_ready || wbeats_valid)
|
|
||||||
out_w.bits := in_w.bits
|
|
||||||
out_w.bits.last := w_last
|
|
||||||
// We should also recreate the last last
|
|
||||||
assert (!out_w.valid || !in_w.bits.last || w_last)
|
|
||||||
|
|
||||||
// R flow control
|
|
||||||
val r_last = out_r.bits.last
|
|
||||||
in_r.valid := out_r.valid && (!r_last || sideband_ar_r.deq.valid)
|
|
||||||
out_r.ready := in_r.ready && (!r_last || sideband_ar_r.deq.valid)
|
|
||||||
sideband_ar_r.deq.ready := r_last && out_r.valid && in_r.ready
|
|
||||||
in_r.bits := out_r.bits
|
|
||||||
in_r.bits.last := r_last && sideband_ar_r.deq.bits
|
|
||||||
|
|
||||||
// B flow control
|
|
||||||
val b_last = sideband_aw_b.deq.bits
|
|
||||||
in_b.valid := out_b.valid && sideband_aw_b.deq.valid && b_last
|
|
||||||
out_b.ready := sideband_aw_b.deq.valid && (!b_last || in_b.ready)
|
|
||||||
sideband_aw_b.deq.ready := out_b.valid && (!b_last || in_b.ready)
|
|
||||||
in_b.bits := out_b.bits
|
|
||||||
|
|
||||||
// Merge errors from dropped B responses
|
|
||||||
val r_resp = RegInit(UInt(0, width = AXI4Parameters.respBits))
|
|
||||||
val resp = out_b.bits.resp | r_resp
|
|
||||||
when (out_b.fire()) { r_resp := Mux(b_last, UInt(0), resp) }
|
|
||||||
in_b.bits.resp := resp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We want to put barriers between the fragments of a fragmented transfer and all other transfers.
|
|
||||||
* This lets us use very little state to reassemble the fragments (else we need one FIFO per ID).
|
|
||||||
* Furthermore, because all the fragments share the same AXI ID, they come back contiguously.
|
|
||||||
* This guarantees that no other R responses might get mixed between fragments, ensuring that the
|
|
||||||
* interleavedId for the slaves remains unaffected by the fragmentation transformation.
|
|
||||||
* Of course, if you need to fragment, this means there is a potentially hefty serialization cost.
|
|
||||||
* However, this design allows full concurrency in the common no-fragmentation-needed scenario.
|
|
||||||
*/
|
|
||||||
class AXI4FragmenterSideband(maxInFlight: Int, flow: Boolean = false) extends Module
|
|
||||||
{
|
|
||||||
val io = new QueueIO(Bool(), maxInFlight)
|
|
||||||
io.count := UInt(0)
|
|
||||||
|
|
||||||
val PASS = UInt(2, width = 2) // allow 'last=1' bits to enque, on 'last=0' if count>0 block else accept+FIND
|
|
||||||
val FIND = UInt(0, width = 2) // allow 'last=0' bits to enque, accept 'last=1' and switch to WAIT
|
|
||||||
val WAIT = UInt(1, width = 2) // block all access till count=0
|
|
||||||
|
|
||||||
val state = RegInit(PASS)
|
|
||||||
val count = RegInit(UInt(0, width = log2Up(maxInFlight)))
|
|
||||||
val full = count === UInt(maxInFlight-1)
|
|
||||||
val empty = count === UInt(0)
|
|
||||||
val last = count === UInt(1)
|
|
||||||
|
|
||||||
io.deq.bits := state(1) || (last && state(0)) // PASS || (last && WAIT)
|
|
||||||
io.deq.valid := !empty
|
|
||||||
|
|
||||||
io.enq.ready := !full && (empty || (state === FIND) || (state === PASS && io.enq.bits))
|
|
||||||
|
|
||||||
// WAIT => count > 0
|
|
||||||
assert (state =/= WAIT || count =/= UInt(0))
|
|
||||||
|
|
||||||
if (flow) {
|
|
||||||
when (io.enq.valid) {
|
|
||||||
io.deq.valid := Bool(true)
|
|
||||||
when (empty) { io.deq.bits := io.enq.bits }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
count := count + io.enq.fire() - io.deq.fire()
|
/* We want to put barriers between the fragments of a fragmented transfer and all other transfers.
|
||||||
switch (state) {
|
* This lets us use very little state to reassemble the fragments (else we need one FIFO per ID).
|
||||||
is(PASS) { when (io.enq.valid && !io.enq.bits && empty) { state := FIND } }
|
* Furthermore, because all the fragments share the same AXI ID, they come back contiguously.
|
||||||
is(FIND) { when (io.enq.valid && io.enq.bits && !full) { state := Mux(empty, PASS, WAIT) } }
|
* This guarantees that no other R responses might get mixed between fragments, ensuring that the
|
||||||
is(WAIT) { when (last && io.deq.ready) { state := PASS } }
|
* interleavedId for the slaves remains unaffected by the fragmentation transformation.
|
||||||
|
* Of course, if you need to fragment, this means there is a potentially hefty serialization cost.
|
||||||
|
* However, this design allows full concurrency in the common no-fragmentation-needed scenario.
|
||||||
|
*/
|
||||||
|
class AXI4FragmenterSideband(maxInFlight: Int, flow: Boolean = false) extends Module
|
||||||
|
{
|
||||||
|
val io = new QueueIO(Bool(), maxInFlight)
|
||||||
|
io.count := UInt(0)
|
||||||
|
|
||||||
|
val PASS = UInt(2, width = 2) // allow 'last=1' bits to enque, on 'last=0' if count>0 block else accept+FIND
|
||||||
|
val FIND = UInt(0, width = 2) // allow 'last=0' bits to enque, accept 'last=1' and switch to WAIT
|
||||||
|
val WAIT = UInt(1, width = 2) // block all access till count=0
|
||||||
|
|
||||||
|
val state = RegInit(PASS)
|
||||||
|
val count = RegInit(UInt(0, width = log2Up(maxInFlight)))
|
||||||
|
val full = count === UInt(maxInFlight-1)
|
||||||
|
val empty = count === UInt(0)
|
||||||
|
val last = count === UInt(1)
|
||||||
|
|
||||||
|
io.deq.bits := state(1) || (last && state(0)) // PASS || (last && WAIT)
|
||||||
|
io.deq.valid := !empty
|
||||||
|
|
||||||
|
io.enq.ready := !full && (empty || (state === FIND) || (state === PASS && io.enq.bits))
|
||||||
|
|
||||||
|
// WAIT => count > 0
|
||||||
|
assert (state =/= WAIT || count =/= UInt(0))
|
||||||
|
|
||||||
|
if (flow) {
|
||||||
|
when (io.enq.valid) {
|
||||||
|
io.deq.valid := Bool(true)
|
||||||
|
when (empty) { io.deq.bits := io.enq.bits }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
count := count + io.enq.fire() - io.deq.fire()
|
||||||
|
switch (state) {
|
||||||
|
is(PASS) { when (io.enq.valid && !io.enq.bits && empty) { state := FIND } }
|
||||||
|
is(FIND) { when (io.enq.valid && io.enq.bits && !full) { state := Mux(empty, PASS, WAIT) } }
|
||||||
|
is(WAIT) { when (last && io.deq.ready) { state := PASS } }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,10 +19,6 @@ object AXI4Imp extends NodeImp[AXI4MasterPortParameters, AXI4SlavePortParameters
|
|||||||
override def labelI(ei: AXI4EdgeParameters) = (ei.slave.beatBytes * 8).toString
|
override def labelI(ei: AXI4EdgeParameters) = (ei.slave.beatBytes * 8).toString
|
||||||
override def labelO(eo: AXI4EdgeParameters) = (eo.slave.beatBytes * 8).toString
|
override def labelO(eo: AXI4EdgeParameters) = (eo.slave.beatBytes * 8).toString
|
||||||
|
|
||||||
def connect(bo: => AXI4Bundle, bi: => AXI4Bundle, ei: => AXI4EdgeParameters)(implicit p: Parameters, sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = {
|
|
||||||
(None, () => { bi <> bo })
|
|
||||||
}
|
|
||||||
|
|
||||||
override def mixO(pd: AXI4MasterPortParameters, node: OutwardNode[AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4Bundle]): AXI4MasterPortParameters =
|
override def mixO(pd: AXI4MasterPortParameters, node: OutwardNode[AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4Bundle]): AXI4MasterPortParameters =
|
||||||
pd.copy(masters = pd.masters.map { c => c.copy (nodePath = node +: c.nodePath) })
|
pd.copy(masters = pd.masters.map { c => c.copy (nodePath = node +: c.nodePath) })
|
||||||
override def mixI(pu: AXI4SlavePortParameters, node: InwardNode[AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4Bundle]): AXI4SlavePortParameters =
|
override def mixI(pu: AXI4SlavePortParameters, node: InwardNode[AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4Bundle]): AXI4SlavePortParameters =
|
||||||
@ -31,16 +27,13 @@ object AXI4Imp extends NodeImp[AXI4MasterPortParameters, AXI4SlavePortParameters
|
|||||||
|
|
||||||
// Nodes implemented inside modules
|
// Nodes implemented inside modules
|
||||||
case class AXI4IdentityNode() extends IdentityNode(AXI4Imp)
|
case class AXI4IdentityNode() extends IdentityNode(AXI4Imp)
|
||||||
case class AXI4MasterNode(portParams: AXI4MasterPortParameters, numPorts: Range.Inclusive = 1 to 1)
|
case class AXI4MasterNode(portParams: Seq[AXI4MasterPortParameters]) extends SourceNode(AXI4Imp)(portParams)
|
||||||
extends SourceNode(AXI4Imp)(portParams, numPorts)
|
case class AXI4SlaveNode(portParams: Seq[AXI4SlavePortParameters]) extends SinkNode(AXI4Imp)(portParams)
|
||||||
case class AXI4SlaveNode(portParams: AXI4SlavePortParameters, numPorts: Range.Inclusive = 1 to 1)
|
|
||||||
extends SinkNode(AXI4Imp)(portParams, numPorts)
|
|
||||||
case class AXI4AdapterNode(
|
case class AXI4AdapterNode(
|
||||||
masterFn: Seq[AXI4MasterPortParameters] => AXI4MasterPortParameters,
|
masterFn: AXI4MasterPortParameters => AXI4MasterPortParameters,
|
||||||
slaveFn: Seq[AXI4SlavePortParameters] => AXI4SlavePortParameters,
|
slaveFn: AXI4SlavePortParameters => AXI4SlavePortParameters,
|
||||||
numMasterPorts: Range.Inclusive = 1 to 1,
|
numPorts: Range.Inclusive = 0 to 999)
|
||||||
numSlavePorts: Range.Inclusive = 1 to 1)
|
extends AdapterNode(AXI4Imp)(masterFn, slaveFn, numPorts)
|
||||||
extends InteriorNode(AXI4Imp)(masterFn, slaveFn, numMasterPorts, numSlavePorts)
|
|
||||||
|
|
||||||
// Nodes passed from an inner module
|
// Nodes passed from an inner module
|
||||||
case class AXI4OutputNode() extends OutputNode(AXI4Imp)
|
case class AXI4OutputNode() extends OutputNode(AXI4Imp)
|
||||||
|
@ -9,7 +9,7 @@ import regmapper._
|
|||||||
import scala.math.{min,max}
|
import scala.math.{min,max}
|
||||||
|
|
||||||
class AXI4RegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false)
|
class AXI4RegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false)
|
||||||
extends AXI4SlaveNode(AXI4SlavePortParameters(
|
extends AXI4SlaveNode(Seq(AXI4SlavePortParameters(
|
||||||
Seq(AXI4SlaveParameters(
|
Seq(AXI4SlaveParameters(
|
||||||
address = Seq(address),
|
address = Seq(address),
|
||||||
executable = executable,
|
executable = executable,
|
||||||
@ -17,7 +17,7 @@ class AXI4RegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int
|
|||||||
supportsRead = TransferSizes(1, beatBytes),
|
supportsRead = TransferSizes(1, beatBytes),
|
||||||
interleavedId = Some(0))),
|
interleavedId = Some(0))),
|
||||||
beatBytes = beatBytes,
|
beatBytes = beatBytes,
|
||||||
minLatency = min(concurrency, 1))) // the Queue adds at most one cycle
|
minLatency = min(concurrency, 1)))) // the Queue adds at most one cycle
|
||||||
{
|
{
|
||||||
require (address.contiguous)
|
require (address.contiguous)
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import diplomacy._
|
|||||||
|
|
||||||
class AXI4RAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule
|
class AXI4RAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule
|
||||||
{
|
{
|
||||||
val node = AXI4SlaveNode(AXI4SlavePortParameters(
|
val node = AXI4SlaveNode(Seq(AXI4SlavePortParameters(
|
||||||
Seq(AXI4SlaveParameters(
|
Seq(AXI4SlaveParameters(
|
||||||
address = List(address),
|
address = List(address),
|
||||||
regionType = RegionType.UNCACHED,
|
regionType = RegionType.UNCACHED,
|
||||||
@ -17,7 +17,7 @@ class AXI4RAM(address: AddressSet, executable: Boolean = true, beatBytes: Int =
|
|||||||
supportsWrite = TransferSizes(1, beatBytes),
|
supportsWrite = TransferSizes(1, beatBytes),
|
||||||
interleavedId = Some(0))),
|
interleavedId = Some(0))),
|
||||||
beatBytes = beatBytes,
|
beatBytes = beatBytes,
|
||||||
minLatency = 0)) // B responds on same cycle
|
minLatency = 0))) // B responds on same cycle
|
||||||
|
|
||||||
// We require the address range to include an entire beat (for the write mask)
|
// We require the address range to include an entire beat (for the write mask)
|
||||||
require ((address.mask & (beatBytes-1)) == beatBytes-1)
|
require ((address.mask & (beatBytes-1)) == beatBytes-1)
|
||||||
|
@ -8,15 +8,15 @@ import config._
|
|||||||
import diplomacy._
|
import diplomacy._
|
||||||
import uncore.tilelink2._
|
import uncore.tilelink2._
|
||||||
|
|
||||||
case class AXI4ToTLNode() extends MixedNode(AXI4Imp, TLImp)(
|
case class AXI4ToTLNode() extends MixedAdapterNode(AXI4Imp, TLImp)(
|
||||||
dFn = { case (1, Seq(AXI4MasterPortParameters(masters))) =>
|
dFn = { case AXI4MasterPortParameters(masters) =>
|
||||||
Seq(TLClientPortParameters(clients = masters.map { m =>
|
TLClientPortParameters(clients = masters.map { m =>
|
||||||
TLClientParameters(
|
TLClientParameters(
|
||||||
sourceId = IdRange(m.id.start << 1, m.id.end << 1), // R+W ids are distinct
|
sourceId = IdRange(m.id.start << 1, m.id.end << 1), // R+W ids are distinct
|
||||||
nodePath = m.nodePath)
|
nodePath = m.nodePath)
|
||||||
}))
|
})
|
||||||
},
|
},
|
||||||
uFn = { case (1, Seq(mp)) => Seq(AXI4SlavePortParameters(
|
uFn = { mp => AXI4SlavePortParameters(
|
||||||
slaves = mp.managers.map { m =>
|
slaves = mp.managers.map { m =>
|
||||||
AXI4SlaveParameters(
|
AXI4SlaveParameters(
|
||||||
address = m.address,
|
address = m.address,
|
||||||
@ -27,10 +27,8 @@ case class AXI4ToTLNode() extends MixedNode(AXI4Imp, TLImp)(
|
|||||||
supportsRead = m.supportsGet,
|
supportsRead = m.supportsGet,
|
||||||
interleavedId = Some(0))}, // TL2 never interleaves D beats
|
interleavedId = Some(0))}, // TL2 never interleaves D beats
|
||||||
beatBytes = mp.beatBytes,
|
beatBytes = mp.beatBytes,
|
||||||
minLatency = mp.minLatency))
|
minLatency = mp.minLatency)
|
||||||
},
|
})
|
||||||
numPO = 1 to 1,
|
|
||||||
numPI = 1 to 1)
|
|
||||||
|
|
||||||
class AXI4ToTL()(implicit p: Parameters) extends LazyModule
|
class AXI4ToTL()(implicit p: Parameters) extends LazyModule
|
||||||
{
|
{
|
||||||
@ -42,131 +40,129 @@ class AXI4ToTL()(implicit p: Parameters) extends LazyModule
|
|||||||
val out = node.bundleOut
|
val out = node.bundleOut
|
||||||
}
|
}
|
||||||
|
|
||||||
val in = io.in(0)
|
((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
|
||||||
val out = io.out(0)
|
val numIds = edgeIn.master.endId
|
||||||
val edgeIn = node.edgesIn(0)
|
val beatBytes = edgeOut.manager.beatBytes
|
||||||
val edgeOut = node.edgesOut(0)
|
val countBits = AXI4Parameters.lenBits + (1 << AXI4Parameters.sizeBits) - 1
|
||||||
val numIds = edgeIn.master.endId
|
|
||||||
val beatBytes = edgeOut.manager.beatBytes
|
|
||||||
val countBits = AXI4Parameters.lenBits + (1 << AXI4Parameters.sizeBits) - 1
|
|
||||||
|
|
||||||
require (edgeIn.master.masters(0).aligned)
|
require (edgeIn.master.masters(0).aligned)
|
||||||
|
|
||||||
val r_out = Wire(out.a)
|
val r_out = Wire(out.a)
|
||||||
val r_inflight = RegInit(UInt(0, width = numIds))
|
val r_inflight = RegInit(UInt(0, width = numIds))
|
||||||
val r_block = r_inflight(in.ar.bits.id)
|
val r_block = r_inflight(in.ar.bits.id)
|
||||||
val r_size1 = in.ar.bits.bytes1()
|
val r_size1 = in.ar.bits.bytes1()
|
||||||
val r_size = OH1ToUInt(r_size1)
|
val r_size = OH1ToUInt(r_size1)
|
||||||
val r_addr = in.ar.bits.addr
|
val r_addr = in.ar.bits.addr
|
||||||
val r_ok = edgeOut.manager.supportsGetSafe(r_addr, r_size)
|
val r_ok = edgeOut.manager.supportsGetSafe(r_addr, r_size)
|
||||||
val r_err_in = Wire(Decoupled(new AXI4BundleRError(in.ar.bits.params)))
|
val r_err_in = Wire(Decoupled(new AXI4BundleRError(in.ar.bits.params)))
|
||||||
val r_err_out = Queue(r_err_in, 2)
|
val r_err_out = Queue(r_err_in, 2)
|
||||||
val r_count = RegInit(UInt(0, width = in.ar.bits.params.lenBits))
|
val r_count = RegInit(UInt(0, width = in.ar.bits.params.lenBits))
|
||||||
val r_last = r_count === in.ar.bits.len
|
val r_last = r_count === in.ar.bits.len
|
||||||
|
|
||||||
assert (!in.ar.valid || r_size1 === UIntToOH1(r_size, countBits)) // because aligned
|
assert (!in.ar.valid || r_size1 === UIntToOH1(r_size, countBits)) // because aligned
|
||||||
in.ar.ready := Mux(r_ok, r_out.ready, r_err_in.ready && r_last) && !r_block
|
in.ar.ready := Mux(r_ok, r_out.ready, r_err_in.ready && r_last) && !r_block
|
||||||
r_out.valid := in.ar.valid && !r_block && r_ok
|
r_out.valid := in.ar.valid && !r_block && r_ok
|
||||||
r_out.bits := edgeOut.Get(in.ar.bits.id << 1 | UInt(1), r_addr, r_size)._2
|
r_out.bits := edgeOut.Get(in.ar.bits.id << 1 | UInt(1), r_addr, r_size)._2
|
||||||
r_err_in.valid := in.ar.valid && !r_block && !r_ok
|
r_err_in.valid := in.ar.valid && !r_block && !r_ok
|
||||||
r_err_in.bits.last := r_last
|
r_err_in.bits.last := r_last
|
||||||
r_err_in.bits.id := in.ar.bits.id
|
r_err_in.bits.id := in.ar.bits.id
|
||||||
|
|
||||||
when (r_err_in.fire()) { r_count := Mux(r_last, UInt(0), r_count + UInt(1)) }
|
when (r_err_in.fire()) { r_count := Mux(r_last, UInt(0), r_count + UInt(1)) }
|
||||||
|
|
||||||
val w_out = Wire(out.a)
|
val w_out = Wire(out.a)
|
||||||
val w_inflight = RegInit(UInt(0, width = numIds))
|
val w_inflight = RegInit(UInt(0, width = numIds))
|
||||||
val w_block = w_inflight(in.aw.bits.id)
|
val w_block = w_inflight(in.aw.bits.id)
|
||||||
val w_size1 = in.aw.bits.bytes1()
|
val w_size1 = in.aw.bits.bytes1()
|
||||||
val w_size = OH1ToUInt(w_size1)
|
val w_size = OH1ToUInt(w_size1)
|
||||||
val w_addr = in.aw.bits.addr
|
val w_addr = in.aw.bits.addr
|
||||||
val w_ok = edgeOut.manager.supportsPutPartialSafe(w_addr, w_size)
|
val w_ok = edgeOut.manager.supportsPutPartialSafe(w_addr, w_size)
|
||||||
val w_err_in = Wire(Decoupled(in.aw.bits.id))
|
val w_err_in = Wire(Decoupled(in.aw.bits.id))
|
||||||
val w_err_out = Queue(w_err_in, 2)
|
val w_err_out = Queue(w_err_in, 2)
|
||||||
|
|
||||||
assert (!in.aw.valid || w_size1 === UIntToOH1(w_size, countBits)) // because aligned
|
assert (!in.aw.valid || w_size1 === UIntToOH1(w_size, countBits)) // because aligned
|
||||||
assert (!in.aw.valid || in.aw.bits.len === UInt(0) || in.aw.bits.size === UInt(log2Ceil(beatBytes))) // because aligned
|
assert (!in.aw.valid || in.aw.bits.len === UInt(0) || in.aw.bits.size === UInt(log2Ceil(beatBytes))) // because aligned
|
||||||
in.aw.ready := Mux(w_ok, w_out.ready, w_err_in.ready) && in.w.valid && in.w.bits.last && !w_block
|
in.aw.ready := Mux(w_ok, w_out.ready, w_err_in.ready) && in.w.valid && in.w.bits.last && !w_block
|
||||||
in.w.ready := Mux(w_ok, w_out.ready, w_err_in.ready || !in.w.bits.last) && in.aw.valid && !w_block
|
in.w.ready := Mux(w_ok, w_out.ready, w_err_in.ready || !in.w.bits.last) && in.aw.valid && !w_block
|
||||||
w_out.valid := in.aw.valid && in.w.valid && !w_block && w_ok
|
w_out.valid := in.aw.valid && in.w.valid && !w_block && w_ok
|
||||||
w_out.bits := edgeOut.Put(in.aw.bits.id << 1, w_addr, w_size, in.w.bits.data, in.w.bits.strb)._2
|
w_out.bits := edgeOut.Put(in.aw.bits.id << 1, w_addr, w_size, in.w.bits.data, in.w.bits.strb)._2
|
||||||
w_err_in.valid := in.aw.valid && in.w.valid && !w_block && !w_ok && in.w.bits.last
|
w_err_in.valid := in.aw.valid && in.w.valid && !w_block && !w_ok && in.w.bits.last
|
||||||
w_err_in.bits := in.aw.bits.id
|
w_err_in.bits := in.aw.bits.id
|
||||||
|
|
||||||
TLArbiter(TLArbiter.lowestIndexFirst)(out.a, (UInt(0), r_out), (in.aw.bits.len, w_out))
|
TLArbiter(TLArbiter.lowestIndexFirst)(out.a, (UInt(0), r_out), (in.aw.bits.len, w_out))
|
||||||
|
|
||||||
val ok_b = Wire(in.b)
|
val ok_b = Wire(in.b)
|
||||||
val err_b = Wire(in.b)
|
val err_b = Wire(in.b)
|
||||||
val mux_b = Wire(in.b)
|
val mux_b = Wire(in.b)
|
||||||
val ok_r = Wire(in.r)
|
val ok_r = Wire(in.r)
|
||||||
val err_r = Wire(in.r)
|
val err_r = Wire(in.r)
|
||||||
val mux_r = Wire(in.r)
|
val mux_r = Wire(in.r)
|
||||||
|
|
||||||
val d_resp = Mux(out.d.bits.error, AXI4Parameters.RESP_SLVERR, AXI4Parameters.RESP_OKAY)
|
val d_resp = Mux(out.d.bits.error, AXI4Parameters.RESP_SLVERR, AXI4Parameters.RESP_OKAY)
|
||||||
val d_hasData = edgeOut.hasData(out.d.bits)
|
val d_hasData = edgeOut.hasData(out.d.bits)
|
||||||
val d_last = edgeOut.last(out.d)
|
val d_last = edgeOut.last(out.d)
|
||||||
|
|
||||||
out.d.ready := Mux(d_hasData, ok_r.ready, ok_b.ready)
|
out.d.ready := Mux(d_hasData, ok_r.ready, ok_b.ready)
|
||||||
ok_r.valid := out.d.valid && d_hasData
|
ok_r.valid := out.d.valid && d_hasData
|
||||||
ok_b.valid := out.d.valid && !d_hasData
|
ok_b.valid := out.d.valid && !d_hasData
|
||||||
|
|
||||||
ok_r.bits.id := out.d.bits.source >> 1
|
ok_r.bits.id := out.d.bits.source >> 1
|
||||||
ok_r.bits.data := out.d.bits.data
|
ok_r.bits.data := out.d.bits.data
|
||||||
ok_r.bits.resp := d_resp
|
ok_r.bits.resp := d_resp
|
||||||
ok_r.bits.last := d_last
|
ok_r.bits.last := d_last
|
||||||
|
|
||||||
r_err_out.ready := err_r.ready
|
r_err_out.ready := err_r.ready
|
||||||
err_r.valid := r_err_out.valid
|
err_r.valid := r_err_out.valid
|
||||||
err_r.bits.id := r_err_out.bits.id
|
err_r.bits.id := r_err_out.bits.id
|
||||||
err_r.bits.data := out.d.bits.data // don't care
|
err_r.bits.data := out.d.bits.data // don't care
|
||||||
err_r.bits.resp := AXI4Parameters.RESP_DECERR
|
err_r.bits.resp := AXI4Parameters.RESP_DECERR
|
||||||
err_r.bits.last := r_err_out.bits.last
|
err_r.bits.last := r_err_out.bits.last
|
||||||
|
|
||||||
// AXI4 must hold R to one source until last
|
// AXI4 must hold R to one source until last
|
||||||
val mux_lock_ok = RegInit(Bool(false))
|
val mux_lock_ok = RegInit(Bool(false))
|
||||||
val mux_lock_err = RegInit(Bool(false))
|
val mux_lock_err = RegInit(Bool(false))
|
||||||
when (ok_r .fire()) { mux_lock_ok := !ok_r .bits.last }
|
when (ok_r .fire()) { mux_lock_ok := !ok_r .bits.last }
|
||||||
when (err_r.fire()) { mux_lock_err := !err_r.bits.last }
|
when (err_r.fire()) { mux_lock_err := !err_r.bits.last }
|
||||||
assert (!mux_lock_ok || !mux_lock_err)
|
assert (!mux_lock_ok || !mux_lock_err)
|
||||||
|
|
||||||
// Prioritize err over ok (b/c err_r.valid comes from a register)
|
// Prioritize err over ok (b/c err_r.valid comes from a register)
|
||||||
mux_r.valid := (!mux_lock_err && ok_r.valid) || (!mux_lock_ok && err_r.valid)
|
mux_r.valid := (!mux_lock_err && ok_r.valid) || (!mux_lock_ok && err_r.valid)
|
||||||
mux_r.bits := Mux(!mux_lock_ok && err_r.valid, err_r.bits, ok_r.bits)
|
mux_r.bits := Mux(!mux_lock_ok && err_r.valid, err_r.bits, ok_r.bits)
|
||||||
ok_r.ready := mux_r.ready && (mux_lock_ok || !err_r.valid)
|
ok_r.ready := mux_r.ready && (mux_lock_ok || !err_r.valid)
|
||||||
err_r.ready := mux_r.ready && !mux_lock_ok
|
err_r.ready := mux_r.ready && !mux_lock_ok
|
||||||
|
|
||||||
// AXI4 needs irrevocable behaviour
|
// AXI4 needs irrevocable behaviour
|
||||||
in.r <> Queue.irrevocable(mux_r, 1, flow=true)
|
in.r <> Queue.irrevocable(mux_r, 1, flow=true)
|
||||||
|
|
||||||
ok_b.bits.id := out.d.bits.source >> 1
|
ok_b.bits.id := out.d.bits.source >> 1
|
||||||
ok_b.bits.resp := d_resp
|
ok_b.bits.resp := d_resp
|
||||||
|
|
||||||
w_err_out.ready := err_b.ready
|
w_err_out.ready := err_b.ready
|
||||||
err_b.valid := w_err_out.valid
|
err_b.valid := w_err_out.valid
|
||||||
err_b.bits.id := w_err_out.bits
|
err_b.bits.id := w_err_out.bits
|
||||||
err_b.bits.resp := AXI4Parameters.RESP_DECERR
|
err_b.bits.resp := AXI4Parameters.RESP_DECERR
|
||||||
|
|
||||||
// Prioritize err over ok (b/c err_b.valid comes from a register)
|
// Prioritize err over ok (b/c err_b.valid comes from a register)
|
||||||
mux_b.valid := ok_b.valid || err_b.valid
|
mux_b.valid := ok_b.valid || err_b.valid
|
||||||
mux_b.bits := Mux(err_b.valid, err_b.bits, ok_b.bits)
|
mux_b.bits := Mux(err_b.valid, err_b.bits, ok_b.bits)
|
||||||
ok_b.ready := mux_b.ready && !err_b.valid
|
ok_b.ready := mux_b.ready && !err_b.valid
|
||||||
err_b.ready := mux_b.ready
|
err_b.ready := mux_b.ready
|
||||||
|
|
||||||
// AXI4 needs irrevocable behaviour
|
// AXI4 needs irrevocable behaviour
|
||||||
in.b <> Queue.irrevocable(mux_b, 1, flow=true)
|
in.b <> Queue.irrevocable(mux_b, 1, flow=true)
|
||||||
|
|
||||||
// Update flight trackers
|
// Update flight trackers
|
||||||
val r_set = in.ar.fire().asUInt << in.ar.bits.id
|
val r_set = in.ar.fire().asUInt << in.ar.bits.id
|
||||||
val r_clr = (in.r.fire() && in.r.bits.last).asUInt << in.r.bits.id
|
val r_clr = (in.r.fire() && in.r.bits.last).asUInt << in.r.bits.id
|
||||||
r_inflight := (r_inflight | r_set) & ~r_clr
|
r_inflight := (r_inflight | r_set) & ~r_clr
|
||||||
val w_set = in.aw.fire().asUInt << in.aw.bits.id
|
val w_set = in.aw.fire().asUInt << in.aw.bits.id
|
||||||
val w_clr = in.b.fire().asUInt << in.b.bits.id
|
val w_clr = in.b.fire().asUInt << in.b.bits.id
|
||||||
w_inflight := (w_inflight | w_set) & ~w_clr
|
w_inflight := (w_inflight | w_set) & ~w_clr
|
||||||
|
|
||||||
// Unused channels
|
// Unused channels
|
||||||
out.b.ready := Bool(true)
|
out.b.ready := Bool(true)
|
||||||
out.c.valid := Bool(false)
|
out.c.valid := Bool(false)
|
||||||
out.e.valid := Bool(false)
|
out.e.valid := Bool(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ class TLPLIC(supervisor: Boolean, maxPriorities: Int, address: BigInt = 0xC00000
|
|||||||
beatBytes = p(rocket.XLen)/8,
|
beatBytes = p(rocket.XLen)/8,
|
||||||
undefZero = false)
|
undefZero = false)
|
||||||
|
|
||||||
val intnode = IntAdapterNode(
|
val intnode = IntNexusNode(
|
||||||
numSourcePorts = 0 to 1024,
|
numSourcePorts = 0 to 1024,
|
||||||
numSinkPorts = 0 to 1024,
|
numSinkPorts = 0 to 1024,
|
||||||
sourceFn = { _ => IntSourcePortParameters(Seq(IntSourceParameters(contextsPerHart))) },
|
sourceFn = { _ => IntSourcePortParameters(Seq(IntSourceParameters(contextsPerHart))) },
|
||||||
|
@ -6,6 +6,7 @@ import Chisel._
|
|||||||
import chisel3.internal.sourceinfo.SourceInfo
|
import chisel3.internal.sourceinfo.SourceInfo
|
||||||
import config._
|
import config._
|
||||||
import diplomacy._
|
import diplomacy._
|
||||||
|
import util.GenericParameterizedBundle
|
||||||
import scala.math.{min,max}
|
import scala.math.{min,max}
|
||||||
|
|
||||||
// Ensures that all downstream RW managers support Atomic operationss.
|
// Ensures that all downstream RW managers support Atomic operationss.
|
||||||
@ -15,8 +16,8 @@ class TLAtomicAutomata(logical: Boolean = true, arithmetic: Boolean = true, conc
|
|||||||
require (concurrency >= 1)
|
require (concurrency >= 1)
|
||||||
|
|
||||||
val node = TLAdapterNode(
|
val node = TLAdapterNode(
|
||||||
clientFn = { case Seq(cp) => require (!cp.unsafeAtomics); cp.copy(unsafeAtomics = true) },
|
clientFn = { case cp => require (!cp.unsafeAtomics); cp.copy(unsafeAtomics = true) },
|
||||||
managerFn = { case Seq(mp) => mp.copy(managers = mp.managers.map { m =>
|
managerFn = { case mp => mp.copy(managers = mp.managers.map { m =>
|
||||||
val ourSupport = TransferSizes(1, mp.beatBytes)
|
val ourSupport = TransferSizes(1, mp.beatBytes)
|
||||||
def widen(x: TransferSizes) = if (passthrough && x.min <= 2*mp.beatBytes) TransferSizes(1, max(mp.beatBytes, x.max)) else ourSupport
|
def widen(x: TransferSizes) = if (passthrough && x.min <= 2*mp.beatBytes) TransferSizes(1, max(mp.beatBytes, x.max)) else ourSupport
|
||||||
val canDoit = m.supportsPutFull.contains(ourSupport) && m.supportsGet.contains(ourSupport)
|
val canDoit = m.supportsPutFull.contains(ourSupport) && m.supportsGet.contains(ourSupport)
|
||||||
@ -33,245 +34,232 @@ class TLAtomicAutomata(logical: Boolean = true, arithmetic: Boolean = true, conc
|
|||||||
val out = node.bundleOut
|
val out = node.bundleOut
|
||||||
}
|
}
|
||||||
|
|
||||||
val in = io.in(0)
|
((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
|
||||||
val out = io.out(0)
|
val managers = edgeOut.manager.managers
|
||||||
val edgeIn = node.edgesIn(0)
|
val beatBytes = edgeOut.manager.beatBytes
|
||||||
val edgeOut = node.edgesOut(0)
|
|
||||||
val managers = edgeOut.manager.managers
|
|
||||||
val beatBytes = edgeOut.manager.beatBytes
|
|
||||||
|
|
||||||
// To which managers are we adding atomic support?
|
// To which managers are we adding atomic support?
|
||||||
val ourSupport = TransferSizes(1, edgeOut.manager.beatBytes)
|
val ourSupport = TransferSizes(1, edgeOut.manager.beatBytes)
|
||||||
val managersNeedingHelp = managers.filter { m =>
|
val managersNeedingHelp = managers.filter { m =>
|
||||||
m.supportsPutFull.contains(ourSupport) &&
|
m.supportsPutFull.contains(ourSupport) &&
|
||||||
m.supportsGet.contains(ourSupport) &&
|
m.supportsGet.contains(ourSupport) &&
|
||||||
((logical && !m.supportsLogical .contains(ourSupport)) ||
|
((logical && !m.supportsLogical .contains(ourSupport)) ||
|
||||||
(arithmetic && !m.supportsArithmetic.contains(ourSupport)) ||
|
(arithmetic && !m.supportsArithmetic.contains(ourSupport)) ||
|
||||||
!passthrough) // we will do atomics for everyone we can
|
!passthrough) // we will do atomics for everyone we can
|
||||||
}
|
|
||||||
// We cannot add atomcis to a non-FIFO manager
|
|
||||||
managersNeedingHelp foreach { m => require (m.fifoId.isDefined) }
|
|
||||||
// We need to preserve FIFO semantics across FIFO domains, not managers
|
|
||||||
// Suppose you have Put(42) Atomic(+1) both inflight; valid results: 42 or 43
|
|
||||||
// If we allow Put(42) Get() Put(+1) concurrent; valid results: 42 43 OR undef
|
|
||||||
// Making non-FIFO work requires waiting for all Acks to come back (=> use FIFOFixer)
|
|
||||||
val domainsNeedingHelp = managersNeedingHelp.map(_.fifoId.get).distinct
|
|
||||||
// Don't overprovision the CAM
|
|
||||||
val camSize = min(domainsNeedingHelp.size, concurrency)
|
|
||||||
// Compact the fifoIds to only those we care about
|
|
||||||
val camFifoIds = managers.map(m => UInt(m.fifoId.map(id => max(0, domainsNeedingHelp.indexOf(id))).getOrElse(0)))
|
|
||||||
|
|
||||||
// CAM entry state machine
|
|
||||||
val FREE = UInt(0) // unused waiting on Atomic from A
|
|
||||||
val GET = UInt(3) // Get sent down A waiting on AccessDataAck from D
|
|
||||||
val AMO = UInt(2) // AccessDataAck sent up D waiting for A availability
|
|
||||||
val ACK = UInt(1) // Put sent down A waiting for PutAck from D
|
|
||||||
|
|
||||||
def helper(select: Seq[Bool], x: Seq[TransferSizes], lgSize: UInt) =
|
|
||||||
if (!passthrough) Bool(false) else
|
|
||||||
if (x.map(_ == x(0)).reduce(_ && _)) x(0).containsLg(lgSize) else
|
|
||||||
Mux1H(select, x.map(_.containsLg(lgSize)))
|
|
||||||
|
|
||||||
// Do we need to do anything at all?
|
|
||||||
if (camSize > 0) {
|
|
||||||
class CAM_S extends Bundle {
|
|
||||||
val state = UInt(width = 2)
|
|
||||||
}
|
|
||||||
class CAM_A extends Bundle {
|
|
||||||
val bits = new TLBundleA(out.a.bits.params)
|
|
||||||
val fifoId = UInt(width = log2Up(domainsNeedingHelp.size))
|
|
||||||
val lut = UInt(width = 4)
|
|
||||||
}
|
|
||||||
class CAM_D extends Bundle {
|
|
||||||
val data = UInt(width = out.a.bits.params.dataBits)
|
|
||||||
}
|
}
|
||||||
|
// We cannot add atomcis to a non-FIFO manager
|
||||||
|
managersNeedingHelp foreach { m => require (m.fifoId.isDefined) }
|
||||||
|
// We need to preserve FIFO semantics across FIFO domains, not managers
|
||||||
|
// Suppose you have Put(42) Atomic(+1) both inflight; valid results: 42 or 43
|
||||||
|
// If we allow Put(42) Get() Put(+1) concurrent; valid results: 42 43 OR undef
|
||||||
|
// Making non-FIFO work requires waiting for all Acks to come back (=> use FIFOFixer)
|
||||||
|
val domainsNeedingHelp = managersNeedingHelp.map(_.fifoId.get).distinct
|
||||||
|
// Don't overprovision the CAM
|
||||||
|
val camSize = min(domainsNeedingHelp.size, concurrency)
|
||||||
|
// Compact the fifoIds to only those we care about
|
||||||
|
val camFifoIds = managers.map(m => UInt(m.fifoId.map(id => max(0, domainsNeedingHelp.indexOf(id))).getOrElse(0)))
|
||||||
|
|
||||||
val initval = Wire(new CAM_S)
|
// CAM entry state machine
|
||||||
initval.state := FREE
|
val FREE = UInt(0) // unused waiting on Atomic from A
|
||||||
val cam_s = RegInit(Vec.fill(camSize)(initval))
|
val GET = UInt(3) // Get sent down A waiting on AccessDataAck from D
|
||||||
val cam_a = Reg(Vec(camSize, new CAM_A))
|
val AMO = UInt(2) // AccessDataAck sent up D waiting for A availability
|
||||||
val cam_d = Reg(Vec(camSize, new CAM_D))
|
val ACK = UInt(1) // Put sent down A waiting for PutAck from D
|
||||||
|
|
||||||
val cam_free = cam_s.map(_.state === FREE)
|
def helper(select: Seq[Bool], x: Seq[TransferSizes], lgSize: UInt) =
|
||||||
val cam_amo = cam_s.map(_.state === AMO)
|
if (!passthrough) Bool(false) else
|
||||||
val cam_abusy = cam_s.map(e => e.state === GET || e.state === AMO) // A is blocked
|
if (x.map(_ == x(0)).reduce(_ && _)) x(0).containsLg(lgSize) else
|
||||||
val cam_dmatch = cam_s.map(e => e.state =/= FREE) // D should inspect these entries
|
Mux1H(select, x.map(_.containsLg(lgSize)))
|
||||||
|
|
||||||
// Can the manager already handle this message?
|
val params = TLAtomicAutomata.CAMParams(out.a.bits.params, domainsNeedingHelp.size)
|
||||||
val a_size = edgeIn.size(in.a.bits)
|
// Do we need to do anything at all?
|
||||||
val a_select = edgeOut.manager.findFast(edgeIn.address(in.a.bits))
|
if (camSize > 0) {
|
||||||
val a_canLogical = helper(a_select, managers.map(_.supportsLogical), a_size)
|
val initval = Wire(new TLAtomicAutomata.CAM_S(params))
|
||||||
val a_canArithmetic = helper(a_select, managers.map(_.supportsArithmetic), a_size)
|
initval.state := FREE
|
||||||
val a_isLogical = in.a.bits.opcode === TLMessages.LogicalData
|
val cam_s = RegInit(Vec.fill(camSize)(initval))
|
||||||
val a_isArithmetic = in.a.bits.opcode === TLMessages.ArithmeticData
|
val cam_a = Reg(Vec(camSize, new TLAtomicAutomata.CAM_A(params)))
|
||||||
val a_isSupported = Mux(a_isLogical, a_canLogical, Mux(a_isArithmetic, a_canArithmetic, Bool(true)))
|
val cam_d = Reg(Vec(camSize, new TLAtomicAutomata.CAM_D(params)))
|
||||||
|
|
||||||
// Must we do a Put?
|
val cam_free = cam_s.map(_.state === FREE)
|
||||||
val a_cam_any_put = cam_amo.reduce(_ || _)
|
val cam_amo = cam_s.map(_.state === AMO)
|
||||||
val a_cam_por_put = cam_amo.scanLeft(Bool(false))(_||_).init
|
val cam_abusy = cam_s.map(e => e.state === GET || e.state === AMO) // A is blocked
|
||||||
val a_cam_sel_put = (cam_amo zip a_cam_por_put) map { case (a, b) => a && !b }
|
val cam_dmatch = cam_s.map(e => e.state =/= FREE) // D should inspect these entries
|
||||||
val a_cam_a = PriorityMux(cam_amo, cam_a)
|
|
||||||
val a_cam_d = PriorityMux(cam_amo, cam_d)
|
|
||||||
val a_a = a_cam_a.bits.data
|
|
||||||
val a_d = a_cam_d.data
|
|
||||||
|
|
||||||
// Does the A request conflict with an inflight AMO?
|
// Can the manager already handle this message?
|
||||||
val a_fifoId = Mux1H(a_select, camFifoIds)
|
val a_size = edgeIn.size(in.a.bits)
|
||||||
val a_cam_busy = (cam_abusy zip cam_a.map(_.fifoId === a_fifoId)) map { case (a,b) => a&&b } reduce (_||_)
|
val a_select = edgeOut.manager.findFast(edgeIn.address(in.a.bits))
|
||||||
|
val a_canLogical = helper(a_select, managers.map(_.supportsLogical), a_size)
|
||||||
|
val a_canArithmetic = helper(a_select, managers.map(_.supportsArithmetic), a_size)
|
||||||
|
val a_isLogical = in.a.bits.opcode === TLMessages.LogicalData
|
||||||
|
val a_isArithmetic = in.a.bits.opcode === TLMessages.ArithmeticData
|
||||||
|
val a_isSupported = Mux(a_isLogical, a_canLogical, Mux(a_isArithmetic, a_canArithmetic, Bool(true)))
|
||||||
|
|
||||||
// (Where) are we are allocating in the CAM?
|
// Must we do a Put?
|
||||||
val a_cam_any_free = cam_free.reduce(_ || _)
|
val a_cam_any_put = cam_amo.reduce(_ || _)
|
||||||
val a_cam_por_free = cam_free.scanLeft(Bool(false))(_||_).init
|
val a_cam_por_put = cam_amo.scanLeft(Bool(false))(_||_).init
|
||||||
val a_cam_sel_free = (cam_free zip a_cam_por_free) map { case (a,b) => a && !b }
|
val a_cam_sel_put = (cam_amo zip a_cam_por_put) map { case (a, b) => a && !b }
|
||||||
|
val a_cam_a = PriorityMux(cam_amo, cam_a)
|
||||||
|
val a_cam_d = PriorityMux(cam_amo, cam_d)
|
||||||
|
val a_a = a_cam_a.bits.data
|
||||||
|
val a_d = a_cam_d.data
|
||||||
|
|
||||||
// Logical AMO
|
// Does the A request conflict with an inflight AMO?
|
||||||
val indexes = Seq.tabulate(beatBytes*8) { i => Cat(a_a(i,i), a_d(i,i)) }
|
val a_fifoId = Mux1H(a_select, camFifoIds)
|
||||||
val logic_out = Cat(indexes.map(x => a_cam_a.lut(x).asUInt).reverse)
|
val a_cam_busy = (cam_abusy zip cam_a.map(_.fifoId === a_fifoId)) map { case (a,b) => a&&b } reduce (_||_)
|
||||||
|
|
||||||
// Arithmetic AMO
|
// (Where) are we are allocating in the CAM?
|
||||||
val unsigned = a_cam_a.bits.param(1)
|
val a_cam_any_free = cam_free.reduce(_ || _)
|
||||||
val take_max = a_cam_a.bits.param(0)
|
val a_cam_por_free = cam_free.scanLeft(Bool(false))(_||_).init
|
||||||
val adder = a_cam_a.bits.param(2)
|
val a_cam_sel_free = (cam_free zip a_cam_por_free) map { case (a,b) => a && !b }
|
||||||
val mask = a_cam_a.bits.mask
|
|
||||||
val signSel = ~(~mask | (mask >> 1))
|
|
||||||
val signbits_a = Cat(Seq.tabulate(beatBytes) { i => a_a(8*i+7,8*i+7) } .reverse)
|
|
||||||
val signbits_d = Cat(Seq.tabulate(beatBytes) { i => a_d(8*i+7,8*i+7) } .reverse)
|
|
||||||
// Move the selected sign bit into the first byte position it will extend
|
|
||||||
val signbit_a = ((signbits_a & signSel) << 1)(beatBytes-1, 0)
|
|
||||||
val signbit_d = ((signbits_d & signSel) << 1)(beatBytes-1, 0)
|
|
||||||
val signext_a = FillInterleaved(8, leftOR(signbit_a))
|
|
||||||
val signext_d = FillInterleaved(8, leftOR(signbit_d))
|
|
||||||
// NOTE: sign-extension does not change the relative ordering in EITHER unsigned or signed arithmetic
|
|
||||||
val wide_mask = FillInterleaved(8, mask)
|
|
||||||
val a_a_ext = (a_a & wide_mask) | signext_a
|
|
||||||
val a_d_ext = (a_d & wide_mask) | signext_d
|
|
||||||
val a_d_inv = Mux(adder, a_d_ext, ~a_d_ext)
|
|
||||||
val adder_out = a_a_ext + a_d_inv
|
|
||||||
val h = 8*beatBytes-1 // now sign-extended; use biggest bit
|
|
||||||
val a_bigger_uneq = unsigned === a_a_ext(h) // result if high bits are unequal
|
|
||||||
val a_bigger = Mux(a_a_ext(h) === a_d_ext(h), !adder_out(h), a_bigger_uneq)
|
|
||||||
val pick_a = take_max === a_bigger
|
|
||||||
val arith_out = Mux(adder, adder_out, Mux(pick_a, a_a, a_d))
|
|
||||||
|
|
||||||
// AMO result data
|
// Logical AMO
|
||||||
val amo_data =
|
val indexes = Seq.tabulate(beatBytes*8) { i => Cat(a_a(i,i), a_d(i,i)) }
|
||||||
if (!logical) arith_out else
|
val logic_out = Cat(indexes.map(x => a_cam_a.lut(x).asUInt).reverse)
|
||||||
if (!arithmetic) logic_out else
|
|
||||||
Mux(a_cam_a.bits.opcode(0), logic_out, arith_out)
|
|
||||||
|
|
||||||
// Potentially mutate the message from inner
|
// Arithmetic AMO
|
||||||
val source_i = Wire(in.a)
|
val unsigned = a_cam_a.bits.param(1)
|
||||||
val a_allow = !a_cam_busy && (a_isSupported || a_cam_any_free)
|
val take_max = a_cam_a.bits.param(0)
|
||||||
in.a.ready := source_i.ready && a_allow
|
val adder = a_cam_a.bits.param(2)
|
||||||
source_i.valid := in.a.valid && a_allow
|
val mask = a_cam_a.bits.mask
|
||||||
source_i.bits := in.a.bits
|
val signSel = ~(~mask | (mask >> 1))
|
||||||
when (!a_isSupported) { // minimal mux difference
|
val signbits_a = Cat(Seq.tabulate(beatBytes) { i => a_a(8*i+7,8*i+7) } .reverse)
|
||||||
source_i.bits.opcode := TLMessages.Get
|
val signbits_d = Cat(Seq.tabulate(beatBytes) { i => a_d(8*i+7,8*i+7) } .reverse)
|
||||||
source_i.bits.param := UInt(0)
|
// Move the selected sign bit into the first byte position it will extend
|
||||||
}
|
val signbit_a = ((signbits_a & signSel) << 1)(beatBytes-1, 0)
|
||||||
|
val signbit_d = ((signbits_d & signSel) << 1)(beatBytes-1, 0)
|
||||||
|
val signext_a = FillInterleaved(8, leftOR(signbit_a))
|
||||||
|
val signext_d = FillInterleaved(8, leftOR(signbit_d))
|
||||||
|
// NOTE: sign-extension does not change the relative ordering in EITHER unsigned or signed arithmetic
|
||||||
|
val wide_mask = FillInterleaved(8, mask)
|
||||||
|
val a_a_ext = (a_a & wide_mask) | signext_a
|
||||||
|
val a_d_ext = (a_d & wide_mask) | signext_d
|
||||||
|
val a_d_inv = Mux(adder, a_d_ext, ~a_d_ext)
|
||||||
|
val adder_out = a_a_ext + a_d_inv
|
||||||
|
val h = 8*beatBytes-1 // now sign-extended; use biggest bit
|
||||||
|
val a_bigger_uneq = unsigned === a_a_ext(h) // result if high bits are unequal
|
||||||
|
val a_bigger = Mux(a_a_ext(h) === a_d_ext(h), !adder_out(h), a_bigger_uneq)
|
||||||
|
val pick_a = take_max === a_bigger
|
||||||
|
val arith_out = Mux(adder, adder_out, Mux(pick_a, a_a, a_d))
|
||||||
|
|
||||||
// Potentially take the message from the CAM
|
// AMO result data
|
||||||
val source_c = Wire(in.a)
|
val amo_data =
|
||||||
source_c.valid := a_cam_any_put
|
if (!logical) arith_out else
|
||||||
source_c.bits := edgeOut.Put(a_cam_a.bits.source, edgeIn.address(a_cam_a.bits), a_cam_a.bits.size, amo_data)._2
|
if (!arithmetic) logic_out else
|
||||||
|
Mux(a_cam_a.bits.opcode(0), logic_out, arith_out)
|
||||||
|
|
||||||
// Finishing an AMO from the CAM has highest priority
|
// Potentially mutate the message from inner
|
||||||
TLArbiter(TLArbiter.lowestIndexFirst)(out.a, (UInt(0), source_c), (edgeOut.numBeats1(in.a.bits), source_i))
|
val source_i = Wire(in.a)
|
||||||
|
val a_allow = !a_cam_busy && (a_isSupported || a_cam_any_free)
|
||||||
|
in.a.ready := source_i.ready && a_allow
|
||||||
|
source_i.valid := in.a.valid && a_allow
|
||||||
|
source_i.bits := in.a.bits
|
||||||
|
when (!a_isSupported) { // minimal mux difference
|
||||||
|
source_i.bits.opcode := TLMessages.Get
|
||||||
|
source_i.bits.param := UInt(0)
|
||||||
|
}
|
||||||
|
|
||||||
// Capture the A state into the CAM
|
// Potentially take the message from the CAM
|
||||||
when (source_i.fire() && !a_isSupported) {
|
val source_c = Wire(in.a)
|
||||||
(a_cam_sel_free zip cam_a) foreach { case (en, r) =>
|
source_c.valid := a_cam_any_put
|
||||||
when (en) {
|
source_c.bits := edgeOut.Put(a_cam_a.bits.source, edgeIn.address(a_cam_a.bits), a_cam_a.bits.size, amo_data)._2
|
||||||
r.fifoId := a_fifoId
|
|
||||||
r.bits := in.a.bits
|
// Finishing an AMO from the CAM has highest priority
|
||||||
r.lut := MuxLookup(in.a.bits.param(1, 0), UInt(0, width = 4), Array(
|
TLArbiter(TLArbiter.lowestIndexFirst)(out.a, (UInt(0), source_c), (edgeOut.numBeats1(in.a.bits), source_i))
|
||||||
TLAtomics.AND -> UInt(0x8),
|
|
||||||
TLAtomics.OR -> UInt(0xe),
|
// Capture the A state into the CAM
|
||||||
TLAtomics.XOR -> UInt(0x6),
|
when (source_i.fire() && !a_isSupported) {
|
||||||
TLAtomics.SWAP -> UInt(0xc)))
|
(a_cam_sel_free zip cam_a) foreach { case (en, r) =>
|
||||||
|
when (en) {
|
||||||
|
r.fifoId := a_fifoId
|
||||||
|
r.bits := in.a.bits
|
||||||
|
r.lut := MuxLookup(in.a.bits.param(1, 0), UInt(0, width = 4), Array(
|
||||||
|
TLAtomics.AND -> UInt(0x8),
|
||||||
|
TLAtomics.OR -> UInt(0xe),
|
||||||
|
TLAtomics.XOR -> UInt(0x6),
|
||||||
|
TLAtomics.SWAP -> UInt(0xc)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(a_cam_sel_free zip cam_s) foreach { case (en, r) =>
|
||||||
|
when (en) {
|
||||||
|
r.state := GET
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(a_cam_sel_free zip cam_s) foreach { case (en, r) =>
|
|
||||||
when (en) {
|
// Advance the put state
|
||||||
r.state := GET
|
when (source_c.fire()) {
|
||||||
|
(a_cam_sel_put zip cam_s) foreach { case (en, r) =>
|
||||||
|
when (en) {
|
||||||
|
r.state := ACK
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Advance the put state
|
// We need to deal with a potential D response in the same cycle as the A request
|
||||||
when (source_c.fire()) {
|
val d_cam_sel_raw = cam_a.map(_.bits.source === in.d.bits.source)
|
||||||
(a_cam_sel_put zip cam_s) foreach { case (en, r) =>
|
val d_cam_sel_match = (d_cam_sel_raw zip cam_dmatch) map { case (a,b) => a&&b }
|
||||||
when (en) {
|
val d_cam_data = Mux1H(d_cam_sel_match, cam_d.map(_.data))
|
||||||
r.state := ACK
|
val d_cam_sel_bypass = if (edgeOut.manager.minLatency > 0) Bool(false) else
|
||||||
|
out.d.bits.source === in.a.bits.source && in.a.valid && !a_isSupported
|
||||||
|
val d_cam_sel = (a_cam_sel_free zip d_cam_sel_match) map { case (a,d) => Mux(d_cam_sel_bypass, a, d) }
|
||||||
|
val d_cam_sel_any = d_cam_sel_bypass || d_cam_sel_match.reduce(_ || _)
|
||||||
|
val d_ackd = out.d.bits.opcode === TLMessages.AccessAckData
|
||||||
|
val d_ack = out.d.bits.opcode === TLMessages.AccessAck
|
||||||
|
|
||||||
|
when (out.d.fire()) {
|
||||||
|
(d_cam_sel zip cam_d) foreach { case (en, r) =>
|
||||||
|
when (en && d_ackd) {
|
||||||
|
r.data := out.d.bits.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(d_cam_sel zip cam_s) foreach { case (en, r) =>
|
||||||
|
when (en) {
|
||||||
|
// Note: it is important that this comes AFTER the := GET, so we can go FREE=>GET=>AMO in one cycle
|
||||||
|
r.state := Mux(d_ackd, AMO, FREE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// We need to deal with a potential D response in the same cycle as the A request
|
val d_drop = d_ackd && d_cam_sel_any
|
||||||
val d_cam_sel_raw = cam_a.map(_.bits.source === in.d.bits.source)
|
val d_replace = d_ack && d_cam_sel_match.reduce(_ || _)
|
||||||
val d_cam_sel_match = (d_cam_sel_raw zip cam_dmatch) map { case (a,b) => a&&b }
|
|
||||||
val d_cam_data = Mux1H(d_cam_sel_match, cam_d.map(_.data))
|
|
||||||
val d_cam_sel_bypass = if (edgeOut.manager.minLatency > 0) Bool(false) else
|
|
||||||
out.d.bits.source === in.a.bits.source && in.a.valid && !a_isSupported
|
|
||||||
val d_cam_sel = (a_cam_sel_free zip d_cam_sel_match) map { case (a,d) => Mux(d_cam_sel_bypass, a, d) }
|
|
||||||
val d_cam_sel_any = d_cam_sel_bypass || d_cam_sel_match.reduce(_ || _)
|
|
||||||
val d_ackd = out.d.bits.opcode === TLMessages.AccessAckData
|
|
||||||
val d_ack = out.d.bits.opcode === TLMessages.AccessAck
|
|
||||||
|
|
||||||
when (out.d.fire()) {
|
in.d.valid := out.d.valid && !d_drop
|
||||||
(d_cam_sel zip cam_d) foreach { case (en, r) =>
|
out.d.ready := in.d.ready || d_drop
|
||||||
when (en && d_ackd) {
|
|
||||||
r.data := out.d.bits.data
|
in.d.bits := out.d.bits
|
||||||
}
|
when (d_replace) { // minimal muxes
|
||||||
}
|
in.d.bits.opcode := TLMessages.AccessAckData
|
||||||
(d_cam_sel zip cam_s) foreach { case (en, r) =>
|
in.d.bits.data := d_cam_data
|
||||||
when (en) {
|
|
||||||
// Note: it is important that this comes AFTER the := GET, so we can go FREE=>GET=>AMO in one cycle
|
|
||||||
r.state := Mux(d_ackd, AMO, FREE)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
out.a.valid := in.a.valid
|
||||||
|
in.a.ready := out.a.ready
|
||||||
|
out.a.bits := in.a.bits
|
||||||
|
|
||||||
|
in.d.valid := out.d.valid
|
||||||
|
out.d.ready := in.d.ready
|
||||||
|
in.d.bits := out.d.bits
|
||||||
}
|
}
|
||||||
|
|
||||||
val d_drop = d_ackd && d_cam_sel_any
|
if (edgeOut.manager.anySupportAcquireB && edgeIn.client.anySupportProbe) {
|
||||||
val d_replace = d_ack && d_cam_sel_match.reduce(_ || _)
|
in.b.valid := out.b.valid
|
||||||
|
out.b.ready := in.b.ready
|
||||||
|
in.b.bits := out.b.bits
|
||||||
|
|
||||||
in.d.valid := out.d.valid && !d_drop
|
out.c.valid := in.c.valid
|
||||||
out.d.ready := in.d.ready || d_drop
|
in.c.ready := out.c.ready
|
||||||
|
out.c.bits := in.c.bits
|
||||||
|
|
||||||
in.d.bits := out.d.bits
|
out.e.valid := in.e.valid
|
||||||
when (d_replace) { // minimal muxes
|
in.e.ready := out.e.ready
|
||||||
in.d.bits.opcode := TLMessages.AccessAckData
|
out.e.bits := in.e.bits
|
||||||
in.d.bits.data := d_cam_data
|
} else {
|
||||||
|
in.b.valid := Bool(false)
|
||||||
|
in.c.ready := Bool(true)
|
||||||
|
in.e.ready := Bool(true)
|
||||||
|
out.b.ready := Bool(true)
|
||||||
|
out.c.valid := Bool(false)
|
||||||
|
out.e.valid := Bool(false)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
out.a.valid := in.a.valid
|
|
||||||
in.a.ready := out.a.ready
|
|
||||||
out.a.bits := in.a.bits
|
|
||||||
|
|
||||||
in.d.valid := out.d.valid
|
|
||||||
out.d.ready := in.d.ready
|
|
||||||
in.d.bits := out.d.bits
|
|
||||||
}
|
|
||||||
|
|
||||||
if (edgeOut.manager.anySupportAcquireB && edgeIn.client.anySupportProbe) {
|
|
||||||
in.b.valid := out.b.valid
|
|
||||||
out.b.ready := in.b.ready
|
|
||||||
in.b.bits := out.b.bits
|
|
||||||
|
|
||||||
out.c.valid := in.c.valid
|
|
||||||
in.c.ready := out.c.ready
|
|
||||||
out.c.bits := in.c.bits
|
|
||||||
|
|
||||||
out.e.valid := in.e.valid
|
|
||||||
in.e.ready := out.e.ready
|
|
||||||
out.e.bits := in.e.bits
|
|
||||||
} else {
|
|
||||||
in.b.valid := Bool(false)
|
|
||||||
in.c.ready := Bool(true)
|
|
||||||
in.e.ready := Bool(true)
|
|
||||||
out.b.ready := Bool(true)
|
|
||||||
out.c.valid := Bool(false)
|
|
||||||
out.e.valid := Bool(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -284,6 +272,20 @@ object TLAtomicAutomata
|
|||||||
atomics.node := x
|
atomics.node := x
|
||||||
atomics.node
|
atomics.node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case class CAMParams(a: TLBundleParameters, domainsNeedingHelp: Int)
|
||||||
|
|
||||||
|
class CAM_S(params: CAMParams) extends GenericParameterizedBundle(params) {
|
||||||
|
val state = UInt(width = 2)
|
||||||
|
}
|
||||||
|
class CAM_A(params: CAMParams) extends GenericParameterizedBundle(params) {
|
||||||
|
val bits = new TLBundleA(params.a)
|
||||||
|
val fifoId = UInt(width = log2Up(params.domainsNeedingHelp))
|
||||||
|
val lut = UInt(width = 4)
|
||||||
|
}
|
||||||
|
class CAM_D(params: CAMParams) extends GenericParameterizedBundle(params) {
|
||||||
|
val data = UInt(width = params.a.dataBits)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Synthesizeable unit tests */
|
/** Synthesizeable unit tests */
|
||||||
|
@ -13,11 +13,11 @@ class TLBroadcast(lineBytes: Int, numTrackers: Int = 4, bufferless: Boolean = fa
|
|||||||
require (numTrackers > 0)
|
require (numTrackers > 0)
|
||||||
|
|
||||||
val node = TLAdapterNode(
|
val node = TLAdapterNode(
|
||||||
clientFn = { case Seq(cp) =>
|
clientFn = { cp =>
|
||||||
cp.copy(clients = Seq(TLClientParameters(
|
cp.copy(clients = Seq(TLClientParameters(
|
||||||
sourceId = IdRange(0, 1 << log2Ceil(cp.endSourceId*4)))))
|
sourceId = IdRange(0, 1 << log2Ceil(cp.endSourceId*4)))))
|
||||||
},
|
},
|
||||||
managerFn = { case Seq(mp) =>
|
managerFn = { mp =>
|
||||||
mp.copy(
|
mp.copy(
|
||||||
endSinkId = numTrackers,
|
endSinkId = numTrackers,
|
||||||
managers = mp.managers.map { m =>
|
managers = mp.managers.map { m =>
|
||||||
@ -56,154 +56,152 @@ class TLBroadcast(lineBytes: Int, numTrackers: Int = 4, bufferless: Boolean = fa
|
|||||||
val out = node.bundleOut
|
val out = node.bundleOut
|
||||||
}
|
}
|
||||||
|
|
||||||
val in = io.in(0)
|
((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
|
||||||
val out = io.out(0)
|
val clients = edgeIn.client.clients
|
||||||
val edgeIn = node.edgesIn(0)
|
val managers = edgeOut.manager.managers
|
||||||
val edgeOut = node.edgesOut(0)
|
val lineShift = log2Ceil(lineBytes)
|
||||||
val clients = edgeIn.client.clients
|
|
||||||
val managers = edgeOut.manager.managers
|
|
||||||
val lineShift = log2Ceil(lineBytes)
|
|
||||||
|
|
||||||
import TLBroadcastConstants._
|
import TLBroadcastConstants._
|
||||||
|
|
||||||
require (lineBytes >= edgeOut.manager.beatBytes)
|
require (lineBytes >= edgeOut.manager.beatBytes)
|
||||||
// For the probe walker, we need to identify all the caches
|
// For the probe walker, we need to identify all the caches
|
||||||
val caches = clients.filter(_.supportsProbe).map(_.sourceId)
|
val caches = clients.filter(_.supportsProbe).map(_.sourceId)
|
||||||
val cache_targets = caches.map(c => UInt(c.start))
|
val cache_targets = caches.map(c => UInt(c.start))
|
||||||
|
|
||||||
// Create the request tracker queues
|
// Create the request tracker queues
|
||||||
val trackers = Seq.tabulate(numTrackers) { id =>
|
val trackers = Seq.tabulate(numTrackers) { id =>
|
||||||
Module(new TLBroadcastTracker(id, lineBytes, log2Up(caches.size+1), bufferless, edgeIn, edgeOut)).io
|
Module(new TLBroadcastTracker(id, lineBytes, log2Up(caches.size+1), bufferless, edgeIn, edgeOut)).io
|
||||||
|
}
|
||||||
|
|
||||||
|
// We always accept E
|
||||||
|
in.e.ready := Bool(true)
|
||||||
|
(trackers zip UIntToOH(in.e.bits.sink).toBools) foreach { case (tracker, select) =>
|
||||||
|
tracker.e_last := select && in.e.fire()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Depending on the high source bits, we might transform D
|
||||||
|
val d_high = log2Ceil(edgeIn.client.endSourceId)
|
||||||
|
val d_what = out.d.bits.source(d_high+1, d_high)
|
||||||
|
val d_drop = d_what === DROP
|
||||||
|
val d_hasData = edgeOut.hasData(out.d.bits)
|
||||||
|
val d_normal = Wire(in.d)
|
||||||
|
val d_trackerOH = Vec(trackers.map { t => !t.idle && t.source === d_normal.bits.source }).asUInt
|
||||||
|
|
||||||
|
assert (!out.d.valid || !d_drop || out.d.bits.opcode === TLMessages.AccessAck)
|
||||||
|
|
||||||
|
out.d.ready := d_normal.ready || d_drop
|
||||||
|
d_normal.valid := out.d.valid && !d_drop
|
||||||
|
d_normal.bits := out.d.bits // truncates source
|
||||||
|
when (d_what(1)) { // TRANSFORM_*
|
||||||
|
d_normal.bits.opcode := Mux(d_hasData, TLMessages.GrantData, TLMessages.ReleaseAck)
|
||||||
|
d_normal.bits.param := Mux(d_hasData, Mux(d_what(0), TLPermissions.toT, TLPermissions.toB), UInt(0))
|
||||||
|
}
|
||||||
|
d_normal.bits.sink := OHToUInt(d_trackerOH)
|
||||||
|
assert (!d_normal.valid || (d_trackerOH.orR() || d_normal.bits.opcode === TLMessages.ReleaseAck))
|
||||||
|
|
||||||
|
// A tracker response is anything neither dropped nor a ReleaseAck
|
||||||
|
val d_response = d_hasData || !d_what(1)
|
||||||
|
val d_last = edgeIn.last(d_normal)
|
||||||
|
(trackers zip d_trackerOH.toBools) foreach { case (tracker, select) =>
|
||||||
|
tracker.d_last := select && d_normal.fire() && d_response && d_last
|
||||||
|
tracker.probedack := select && out.d.fire() && d_drop
|
||||||
|
}
|
||||||
|
|
||||||
|
// Incoming C can be:
|
||||||
|
// ProbeAck => decrement tracker, drop
|
||||||
|
// ProbeAckData => decrement tracker, send out A as PutFull(DROP)
|
||||||
|
// ReleaseData => send out A as PutFull(TRANSFORM)
|
||||||
|
// Release => send out D as ReleaseAck
|
||||||
|
|
||||||
|
val c_probeack = in.c.bits.opcode === TLMessages.ProbeAck
|
||||||
|
val c_probeackdata = in.c.bits.opcode === TLMessages.ProbeAckData
|
||||||
|
val c_releasedata = in.c.bits.opcode === TLMessages.ReleaseData
|
||||||
|
val c_release = in.c.bits.opcode === TLMessages.Release
|
||||||
|
val c_trackerOH = trackers.map { t => t.line === (in.c.bits.address >> lineShift) }
|
||||||
|
val c_trackerSrc = Mux1H(c_trackerOH, trackers.map { _.source })
|
||||||
|
|
||||||
|
// Decrement the tracker's outstanding probe counter
|
||||||
|
(trackers zip c_trackerOH) foreach { case (tracker, select) =>
|
||||||
|
tracker.probenack := in.c.fire() && c_probeack && select
|
||||||
|
}
|
||||||
|
|
||||||
|
val releaseack = Wire(in.d)
|
||||||
|
val putfull = Wire(out.a)
|
||||||
|
|
||||||
|
in.c.ready := c_probeack || Mux(c_release, releaseack.ready, putfull.ready)
|
||||||
|
|
||||||
|
releaseack.valid := in.c.valid && c_release
|
||||||
|
releaseack.bits := edgeIn.ReleaseAck(in.c.bits.address, UInt(0), in.c.bits.source, in.c.bits.size)
|
||||||
|
|
||||||
|
val put_what = Mux(c_releasedata, TRANSFORM_B, DROP)
|
||||||
|
val put_who = Mux(c_releasedata, in.c.bits.source, c_trackerSrc)
|
||||||
|
putfull.valid := in.c.valid && (c_probeackdata || c_releasedata)
|
||||||
|
putfull.bits := edgeOut.Put(Cat(put_what, put_who), in.c.bits.address, in.c.bits.size, in.c.bits.data)._2
|
||||||
|
|
||||||
|
// Combine ReleaseAck or the modified D
|
||||||
|
TLArbiter.lowest(edgeOut, in.d, releaseack, d_normal)
|
||||||
|
// Combine the PutFull with the trackers
|
||||||
|
TLArbiter.lowestFromSeq(edgeOut, out.a, putfull +: trackers.map(_.out_a))
|
||||||
|
|
||||||
|
// The Probe FSM walks all caches and probes them
|
||||||
|
val probe_todo = RegInit(UInt(0, width = max(1, caches.size)))
|
||||||
|
val probe_line = Reg(UInt())
|
||||||
|
val probe_perms = Reg(UInt(width = 2))
|
||||||
|
val probe_next = probe_todo & ~(leftOR(probe_todo) << 1)
|
||||||
|
val probe_busy = probe_todo.orR()
|
||||||
|
val probe_target = if (caches.size == 0) UInt(0) else Mux1H(probe_next, cache_targets)
|
||||||
|
|
||||||
|
// Probe whatever the FSM wants to do next
|
||||||
|
in.b.valid := probe_busy
|
||||||
|
if (caches.size != 0) {
|
||||||
|
in.b.bits := edgeIn.Probe(probe_line << lineShift, probe_target, UInt(lineShift), probe_perms)._2
|
||||||
|
}
|
||||||
|
when (in.b.fire()) { probe_todo := probe_todo & ~probe_next }
|
||||||
|
|
||||||
|
// Which cache does a request come from?
|
||||||
|
val a_cache = if (caches.size == 0) UInt(1) else Vec(caches.map(_.contains(in.a.bits.source))).asUInt
|
||||||
|
val a_first = edgeIn.first(in.a)
|
||||||
|
|
||||||
|
// To accept a request from A, the probe FSM must be idle and there must be a matching tracker
|
||||||
|
val freeTrackers = Vec(trackers.map { t => t.idle }).asUInt
|
||||||
|
val freeTracker = freeTrackers.orR()
|
||||||
|
val matchTrackers = Vec(trackers.map { t => t.line === in.a.bits.address >> lineShift }).asUInt
|
||||||
|
val matchTracker = matchTrackers.orR()
|
||||||
|
val allocTracker = freeTrackers & ~(leftOR(freeTrackers) << 1)
|
||||||
|
val selectTracker = Mux(matchTracker, matchTrackers, allocTracker)
|
||||||
|
|
||||||
|
val trackerReady = Vec(trackers.map(_.in_a.ready)).asUInt
|
||||||
|
in.a.ready := (!a_first || !probe_busy) && (selectTracker & trackerReady).orR()
|
||||||
|
(trackers zip selectTracker.toBools) foreach { case (t, select) =>
|
||||||
|
t.in_a.valid := in.a.valid && select && (!a_first || !probe_busy)
|
||||||
|
t.in_a.bits := in.a.bits
|
||||||
|
t.in_a_first := a_first
|
||||||
|
t.probe := (if (caches.size == 0) UInt(0) else Mux(a_cache.orR(), UInt(caches.size-1), UInt(caches.size)))
|
||||||
|
}
|
||||||
|
|
||||||
|
when (in.a.fire() && a_first) {
|
||||||
|
probe_todo := ~a_cache // probe all but the cache who poked us
|
||||||
|
probe_line := in.a.bits.address >> lineShift
|
||||||
|
probe_perms := MuxLookup(in.a.bits.opcode, Wire(UInt(width = 2)), Array(
|
||||||
|
TLMessages.PutFullData -> TLPermissions.toN,
|
||||||
|
TLMessages.PutPartialData -> TLPermissions.toN,
|
||||||
|
TLMessages.ArithmeticData -> TLPermissions.toN,
|
||||||
|
TLMessages.LogicalData -> TLPermissions.toN,
|
||||||
|
TLMessages.Get -> TLPermissions.toB,
|
||||||
|
TLMessages.Hint -> MuxLookup(in.a.bits.param, Wire(UInt(width = 2)), Array(
|
||||||
|
TLHints.PREFETCH_READ -> TLPermissions.toB,
|
||||||
|
TLHints.PREFETCH_WRITE -> TLPermissions.toN)),
|
||||||
|
TLMessages.Acquire -> MuxLookup(in.a.bits.param, Wire(UInt(width = 2)), Array(
|
||||||
|
TLPermissions.NtoB -> TLPermissions.toB,
|
||||||
|
TLPermissions.NtoT -> TLPermissions.toN,
|
||||||
|
TLPermissions.BtoT -> TLPermissions.toN))))
|
||||||
|
}
|
||||||
|
|
||||||
|
// The outer TL connections may not be cached
|
||||||
|
out.b.ready := Bool(true)
|
||||||
|
out.c.valid := Bool(false)
|
||||||
|
out.e.valid := Bool(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We always accept E
|
|
||||||
in.e.ready := Bool(true)
|
|
||||||
(trackers zip UIntToOH(in.e.bits.sink).toBools) foreach { case (tracker, select) =>
|
|
||||||
tracker.e_last := select && in.e.fire()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Depending on the high source bits, we might transform D
|
|
||||||
val d_high = log2Ceil(edgeIn.client.endSourceId)
|
|
||||||
val d_what = out.d.bits.source(d_high+1, d_high)
|
|
||||||
val d_drop = d_what === DROP
|
|
||||||
val d_hasData = edgeOut.hasData(out.d.bits)
|
|
||||||
val d_normal = Wire(in.d)
|
|
||||||
val d_trackerOH = Vec(trackers.map { t => !t.idle && t.source === d_normal.bits.source }).asUInt
|
|
||||||
|
|
||||||
assert (!out.d.valid || !d_drop || out.d.bits.opcode === TLMessages.AccessAck)
|
|
||||||
|
|
||||||
out.d.ready := d_normal.ready || d_drop
|
|
||||||
d_normal.valid := out.d.valid && !d_drop
|
|
||||||
d_normal.bits := out.d.bits // truncates source
|
|
||||||
when (d_what(1)) { // TRANSFORM_*
|
|
||||||
d_normal.bits.opcode := Mux(d_hasData, TLMessages.GrantData, TLMessages.ReleaseAck)
|
|
||||||
d_normal.bits.param := Mux(d_hasData, Mux(d_what(0), TLPermissions.toT, TLPermissions.toB), UInt(0))
|
|
||||||
}
|
|
||||||
d_normal.bits.sink := OHToUInt(d_trackerOH)
|
|
||||||
assert (!d_normal.valid || (d_trackerOH.orR() || d_normal.bits.opcode === TLMessages.ReleaseAck))
|
|
||||||
|
|
||||||
// A tracker response is anything neither dropped nor a ReleaseAck
|
|
||||||
val d_response = d_hasData || !d_what(1)
|
|
||||||
val d_last = edgeIn.last(d_normal)
|
|
||||||
(trackers zip d_trackerOH.toBools) foreach { case (tracker, select) =>
|
|
||||||
tracker.d_last := select && d_normal.fire() && d_response && d_last
|
|
||||||
tracker.probedack := select && out.d.fire() && d_drop
|
|
||||||
}
|
|
||||||
|
|
||||||
// Incoming C can be:
|
|
||||||
// ProbeAck => decrement tracker, drop
|
|
||||||
// ProbeAckData => decrement tracker, send out A as PutFull(DROP)
|
|
||||||
// ReleaseData => send out A as PutFull(TRANSFORM)
|
|
||||||
// Release => send out D as ReleaseAck
|
|
||||||
|
|
||||||
val c_probeack = in.c.bits.opcode === TLMessages.ProbeAck
|
|
||||||
val c_probeackdata = in.c.bits.opcode === TLMessages.ProbeAckData
|
|
||||||
val c_releasedata = in.c.bits.opcode === TLMessages.ReleaseData
|
|
||||||
val c_release = in.c.bits.opcode === TLMessages.Release
|
|
||||||
val c_trackerOH = trackers.map { t => t.line === (in.c.bits.address >> lineShift) }
|
|
||||||
val c_trackerSrc = Mux1H(c_trackerOH, trackers.map { _.source })
|
|
||||||
|
|
||||||
// Decrement the tracker's outstanding probe counter
|
|
||||||
(trackers zip c_trackerOH) foreach { case (tracker, select) =>
|
|
||||||
tracker.probenack := in.c.fire() && c_probeack && select
|
|
||||||
}
|
|
||||||
|
|
||||||
val releaseack = Wire(in.d)
|
|
||||||
val putfull = Wire(out.a)
|
|
||||||
|
|
||||||
in.c.ready := c_probeack || Mux(c_release, releaseack.ready, putfull.ready)
|
|
||||||
|
|
||||||
releaseack.valid := in.c.valid && c_release
|
|
||||||
releaseack.bits := edgeIn.ReleaseAck(in.c.bits.address, UInt(0), in.c.bits.source, in.c.bits.size)
|
|
||||||
|
|
||||||
val put_what = Mux(c_releasedata, TRANSFORM_B, DROP)
|
|
||||||
val put_who = Mux(c_releasedata, in.c.bits.source, c_trackerSrc)
|
|
||||||
putfull.valid := in.c.valid && (c_probeackdata || c_releasedata)
|
|
||||||
putfull.bits := edgeOut.Put(Cat(put_what, put_who), in.c.bits.address, in.c.bits.size, in.c.bits.data)._2
|
|
||||||
|
|
||||||
// Combine ReleaseAck or the modified D
|
|
||||||
TLArbiter.lowest(edgeOut, in.d, releaseack, d_normal)
|
|
||||||
// Combine the PutFull with the trackers
|
|
||||||
TLArbiter.lowestFromSeq(edgeOut, out.a, putfull +: trackers.map(_.out_a))
|
|
||||||
|
|
||||||
// The Probe FSM walks all caches and probes them
|
|
||||||
val probe_todo = RegInit(UInt(0, width = max(1, caches.size)))
|
|
||||||
val probe_line = Reg(UInt())
|
|
||||||
val probe_perms = Reg(UInt(width = 2))
|
|
||||||
val probe_next = probe_todo & ~(leftOR(probe_todo) << 1)
|
|
||||||
val probe_busy = probe_todo.orR()
|
|
||||||
val probe_target = if (caches.size == 0) UInt(0) else Mux1H(probe_next, cache_targets)
|
|
||||||
|
|
||||||
// Probe whatever the FSM wants to do next
|
|
||||||
in.b.valid := probe_busy
|
|
||||||
if (caches.size != 0) {
|
|
||||||
in.b.bits := edgeIn.Probe(probe_line << lineShift, probe_target, UInt(lineShift), probe_perms)._2
|
|
||||||
}
|
|
||||||
when (in.b.fire()) { probe_todo := probe_todo & ~probe_next }
|
|
||||||
|
|
||||||
// Which cache does a request come from?
|
|
||||||
val a_cache = if (caches.size == 0) UInt(1) else Vec(caches.map(_.contains(in.a.bits.source))).asUInt
|
|
||||||
val a_first = edgeIn.first(in.a)
|
|
||||||
|
|
||||||
// To accept a request from A, the probe FSM must be idle and there must be a matching tracker
|
|
||||||
val freeTrackers = Vec(trackers.map { t => t.idle }).asUInt
|
|
||||||
val freeTracker = freeTrackers.orR()
|
|
||||||
val matchTrackers = Vec(trackers.map { t => t.line === in.a.bits.address >> lineShift }).asUInt
|
|
||||||
val matchTracker = matchTrackers.orR()
|
|
||||||
val allocTracker = freeTrackers & ~(leftOR(freeTrackers) << 1)
|
|
||||||
val selectTracker = Mux(matchTracker, matchTrackers, allocTracker)
|
|
||||||
|
|
||||||
val trackerReady = Vec(trackers.map(_.in_a.ready)).asUInt
|
|
||||||
in.a.ready := (!a_first || !probe_busy) && (selectTracker & trackerReady).orR()
|
|
||||||
(trackers zip selectTracker.toBools) foreach { case (t, select) =>
|
|
||||||
t.in_a.valid := in.a.valid && select && (!a_first || !probe_busy)
|
|
||||||
t.in_a.bits := in.a.bits
|
|
||||||
t.in_a_first := a_first
|
|
||||||
t.probe := (if (caches.size == 0) UInt(0) else Mux(a_cache.orR(), UInt(caches.size-1), UInt(caches.size)))
|
|
||||||
}
|
|
||||||
|
|
||||||
when (in.a.fire() && a_first) {
|
|
||||||
probe_todo := ~a_cache // probe all but the cache who poked us
|
|
||||||
probe_line := in.a.bits.address >> lineShift
|
|
||||||
probe_perms := MuxLookup(in.a.bits.opcode, Wire(UInt(width = 2)), Array(
|
|
||||||
TLMessages.PutFullData -> TLPermissions.toN,
|
|
||||||
TLMessages.PutPartialData -> TLPermissions.toN,
|
|
||||||
TLMessages.ArithmeticData -> TLPermissions.toN,
|
|
||||||
TLMessages.LogicalData -> TLPermissions.toN,
|
|
||||||
TLMessages.Get -> TLPermissions.toB,
|
|
||||||
TLMessages.Hint -> MuxLookup(in.a.bits.param, Wire(UInt(width = 2)), Array(
|
|
||||||
TLHints.PREFETCH_READ -> TLPermissions.toB,
|
|
||||||
TLHints.PREFETCH_WRITE -> TLPermissions.toN)),
|
|
||||||
TLMessages.Acquire -> MuxLookup(in.a.bits.param, Wire(UInt(width = 2)), Array(
|
|
||||||
TLPermissions.NtoB -> TLPermissions.toB,
|
|
||||||
TLPermissions.NtoT -> TLPermissions.toN,
|
|
||||||
TLPermissions.BtoT -> TLPermissions.toN))))
|
|
||||||
}
|
|
||||||
|
|
||||||
// The outer TL connections may not be cached
|
|
||||||
out.b.ready := Bool(true)
|
|
||||||
out.c.valid := Bool(false)
|
|
||||||
out.e.valid := Bool(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,8 +18,8 @@ class TLBuffer(a: Int = 2, b: Int = 2, c: Int = 2, d: Int = 2, e: Int = 2, pipe:
|
|||||||
require (e >= 0)
|
require (e >= 0)
|
||||||
|
|
||||||
val node = TLAdapterNode(
|
val node = TLAdapterNode(
|
||||||
clientFn = { case Seq(p) => p.copy(minLatency = p.minLatency + min(1,b) + min(1,c)) },
|
clientFn = { p => p.copy(minLatency = p.minLatency + min(1,b) + min(1,c)) },
|
||||||
managerFn = { case Seq(p) => p.copy(minLatency = p.minLatency + min(1,a) + min(1,d)) })
|
managerFn = { p => p.copy(minLatency = p.minLatency + min(1,a) + min(1,d)) })
|
||||||
|
|
||||||
lazy val module = new LazyModuleImp(this) {
|
lazy val module = new LazyModuleImp(this) {
|
||||||
val io = new Bundle {
|
val io = new Bundle {
|
||||||
|
@ -210,11 +210,11 @@ final class DecoupledSnoop[+T <: Data](gen: T) extends Bundle
|
|||||||
|
|
||||||
object DecoupledSnoop
|
object DecoupledSnoop
|
||||||
{
|
{
|
||||||
def apply[T <: Data](i: DecoupledIO[T]) = {
|
def apply[T <: Data](source: DecoupledIO[T], sink: DecoupledIO[T]) = {
|
||||||
val out = Wire(new DecoupledSnoop(i.bits))
|
val out = Wire(new DecoupledSnoop(sink.bits))
|
||||||
out.ready := i.ready
|
out.ready := sink.ready
|
||||||
out.valid := i.valid
|
out.valid := source.valid
|
||||||
out.bits := i.bits
|
out.bits := source.bits
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -230,13 +230,13 @@ class TLBundleSnoop(params: TLBundleParameters) extends TLBundleBase(params)
|
|||||||
|
|
||||||
object TLBundleSnoop
|
object TLBundleSnoop
|
||||||
{
|
{
|
||||||
def apply(x: TLBundle) = {
|
def apply(source: TLBundle, sink: TLBundle) = {
|
||||||
val out = Wire(new TLBundleSnoop(x.params))
|
val out = Wire(new TLBundleSnoop(sink.params))
|
||||||
out.a <> DecoupledSnoop(x.a)
|
out.a := DecoupledSnoop(source.a, sink.a)
|
||||||
out.b <> DecoupledSnoop(x.b)
|
out.b := DecoupledSnoop(sink.b, source.b)
|
||||||
out.c <> DecoupledSnoop(x.c)
|
out.c := DecoupledSnoop(source.c, sink.c)
|
||||||
out.d <> DecoupledSnoop(x.d)
|
out.d := DecoupledSnoop(sink.d, source.d)
|
||||||
out.e <> DecoupledSnoop(x.e)
|
out.e := DecoupledSnoop(source.e, sink.e)
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,10 +12,10 @@ import TLMessages._
|
|||||||
class TLCacheCork(unsafe: Boolean = false)(implicit p: Parameters) extends LazyModule
|
class TLCacheCork(unsafe: Boolean = false)(implicit p: Parameters) extends LazyModule
|
||||||
{
|
{
|
||||||
val node = TLAdapterNode(
|
val node = TLAdapterNode(
|
||||||
clientFn = { case Seq(cp) =>
|
clientFn = { case cp =>
|
||||||
cp.copy(clients = cp.clients.map { c => c.copy(
|
cp.copy(clients = cp.clients.map { c => c.copy(
|
||||||
sourceId = IdRange(c.sourceId.start*2, c.sourceId.end*2))})},
|
sourceId = IdRange(c.sourceId.start*2, c.sourceId.end*2))})},
|
||||||
managerFn = { case Seq(mp) =>
|
managerFn = { case mp =>
|
||||||
mp.copy(managers = mp.managers.map { m => m.copy(
|
mp.copy(managers = mp.managers.map { m => m.copy(
|
||||||
regionType = if (m.regionType == RegionType.UNCACHED) RegionType.TRACKED else m.regionType,
|
regionType = if (m.regionType == RegionType.UNCACHED) RegionType.TRACKED else m.regionType,
|
||||||
supportsAcquireB = m.supportsGet,
|
supportsAcquireB = m.supportsGet,
|
||||||
@ -27,93 +27,89 @@ class TLCacheCork(unsafe: Boolean = false)(implicit p: Parameters) extends LazyM
|
|||||||
val out = node.bundleOut
|
val out = node.bundleOut
|
||||||
}
|
}
|
||||||
|
|
||||||
val edgeIn = node.edgesIn(0)
|
((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
|
||||||
val edgeOut = node.edgesOut(0)
|
require (edgeIn.client.clients.size == 1 || unsafe, "Only one client can safely use a TLCacheCork")
|
||||||
|
require (edgeIn.client.clients.filter(_.supportsProbe).size == 1, "Only one caching client allowed")
|
||||||
|
edgeOut.manager.managers.foreach { case m =>
|
||||||
|
require (!m.supportsAcquireB, "Cannot support caches beyond the Cork")
|
||||||
|
}
|
||||||
|
|
||||||
require (edgeIn.client.clients.size == 1 || unsafe, "Only one client can safely use a TLCacheCork")
|
// The Cork turns [Acquire=>Get] => [AccessAckData=>GrantData]
|
||||||
require (edgeIn.client.clients.filter(_.supportsProbe).size == 1, "Only one caching client allowed")
|
// and [ReleaseData=>PutFullData] => [AccessAck=>ReleaseAck]
|
||||||
edgeOut.manager.managers.foreach { case m =>
|
// We need to encode information sufficient to reverse the transformation in output.
|
||||||
require (!m.supportsAcquireB, "Cannot support caches beyond the Cork")
|
// A caveat is that we get Acquire+Release with the same source and must keep the
|
||||||
|
// source unique after transformation onto the A channel.
|
||||||
|
// The coding scheme is:
|
||||||
|
// Put: 1, Release: 0 => AccessAck
|
||||||
|
// *: 0, Acquire: 1 => AccessAckData
|
||||||
|
|
||||||
|
// Take requests from A to A
|
||||||
|
val isPut = in.a.bits.opcode === PutFullData || in.a.bits.opcode === PutPartialData
|
||||||
|
val a_a = Wire(out.a)
|
||||||
|
a_a <> in.a
|
||||||
|
a_a.bits.source := in.a.bits.source << 1 | Mux(isPut, UInt(1), UInt(0))
|
||||||
|
|
||||||
|
// Transform Acquire into Get
|
||||||
|
when (in.a.bits.opcode === Acquire) {
|
||||||
|
a_a.bits.opcode := Get
|
||||||
|
a_a.bits.param := UInt(0)
|
||||||
|
a_a.bits.source := in.a.bits.source << 1 | UInt(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take ReleaseData from C to A; Release from C to D
|
||||||
|
val c_a = Wire(out.a)
|
||||||
|
c_a.valid := in.c.valid && in.c.bits.opcode === ReleaseData
|
||||||
|
c_a.bits.opcode := PutFullData
|
||||||
|
c_a.bits.param := UInt(0)
|
||||||
|
c_a.bits.size := in.c.bits.size
|
||||||
|
c_a.bits.source := in.c.bits.source << 1
|
||||||
|
c_a.bits.address := in.c.bits.address
|
||||||
|
c_a.bits.mask := edgeOut.mask(in.c.bits.address, in.c.bits.size)
|
||||||
|
c_a.bits.data := in.c.bits.data
|
||||||
|
|
||||||
|
val c_d = Wire(in.d)
|
||||||
|
c_d.valid := in.c.valid && in.c.bits.opcode === Release
|
||||||
|
c_d.bits.opcode := ReleaseAck
|
||||||
|
c_d.bits.param := UInt(0)
|
||||||
|
c_d.bits.size := in.c.bits.size
|
||||||
|
c_d.bits.source := in.c.bits.source
|
||||||
|
c_d.bits.sink := UInt(0)
|
||||||
|
c_d.bits.addr_lo := in.c.bits.address
|
||||||
|
c_d.bits.data := UInt(0)
|
||||||
|
c_d.bits.error := Bool(false)
|
||||||
|
|
||||||
|
assert (!in.c.valid || in.c.bits.opcode === Release || in.c.bits.opcode === ReleaseData)
|
||||||
|
in.c.ready := Mux(in.c.bits.opcode === Release, c_d.ready, c_a.ready)
|
||||||
|
|
||||||
|
// Discard E
|
||||||
|
in.e.ready := Bool(true)
|
||||||
|
|
||||||
|
// Block B; should never happen
|
||||||
|
out.b.ready := Bool(false)
|
||||||
|
assert (!out.b.valid)
|
||||||
|
|
||||||
|
// Take responses from D and transform them
|
||||||
|
val d_d = Wire(in.d)
|
||||||
|
d_d <> out.d
|
||||||
|
d_d.bits.source := out.d.bits.source >> 1
|
||||||
|
|
||||||
|
when (out.d.bits.opcode === AccessAckData && out.d.bits.source(0)) {
|
||||||
|
d_d.bits.opcode := GrantData
|
||||||
|
d_d.bits.param := TLPermissions.toT
|
||||||
|
}
|
||||||
|
when (out.d.bits.opcode === AccessAck && !out.d.bits.source(0)) {
|
||||||
|
d_d.bits.opcode := ReleaseAck
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combine the sources of messages into the channels
|
||||||
|
TLArbiter(TLArbiter.lowestIndexFirst)(out.a, (edgeOut.numBeats1(c_a.bits), c_a), (edgeOut.numBeats1(a_a.bits), a_a))
|
||||||
|
TLArbiter(TLArbiter.lowestIndexFirst)(in.d, (edgeIn .numBeats1(d_d.bits), d_d), (UInt(0), Queue(c_d, 2)))
|
||||||
|
|
||||||
|
// Tie off unused ports
|
||||||
|
in.b.valid := Bool(false)
|
||||||
|
out.c.valid := Bool(false)
|
||||||
|
out.e.valid := Bool(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
val out = io.out(0)
|
|
||||||
val in = io.in(0)
|
|
||||||
|
|
||||||
// The Cork turns [Acquire=>Get] => [AccessAckData=>GrantData]
|
|
||||||
// and [ReleaseData=>PutFullData] => [AccessAck=>ReleaseAck]
|
|
||||||
// We need to encode information sufficient to reverse the transformation in output.
|
|
||||||
// A caveat is that we get Acquire+Release with the same source and must keep the
|
|
||||||
// source unique after transformation onto the A channel.
|
|
||||||
// The coding scheme is:
|
|
||||||
// Put: 1, Release: 0 => AccessAck
|
|
||||||
// *: 0, Acquire: 1 => AccessAckData
|
|
||||||
|
|
||||||
// Take requests from A to A
|
|
||||||
val isPut = in.a.bits.opcode === PutFullData || in.a.bits.opcode === PutPartialData
|
|
||||||
val a_a = Wire(out.a)
|
|
||||||
a_a <> in.a
|
|
||||||
a_a.bits.source := in.a.bits.source << 1 | Mux(isPut, UInt(1), UInt(0))
|
|
||||||
|
|
||||||
// Transform Acquire into Get
|
|
||||||
when (in.a.bits.opcode === Acquire) {
|
|
||||||
a_a.bits.opcode := Get
|
|
||||||
a_a.bits.param := UInt(0)
|
|
||||||
a_a.bits.source := in.a.bits.source << 1 | UInt(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take ReleaseData from C to A; Release from C to D
|
|
||||||
val c_a = Wire(out.a)
|
|
||||||
c_a.valid := in.c.valid && in.c.bits.opcode === ReleaseData
|
|
||||||
c_a.bits.opcode := PutFullData
|
|
||||||
c_a.bits.param := UInt(0)
|
|
||||||
c_a.bits.size := in.c.bits.size
|
|
||||||
c_a.bits.source := in.c.bits.source << 1
|
|
||||||
c_a.bits.address := in.c.bits.address
|
|
||||||
c_a.bits.mask := edgeOut.mask(in.c.bits.address, in.c.bits.size)
|
|
||||||
c_a.bits.data := in.c.bits.data
|
|
||||||
|
|
||||||
val c_d = Wire(in.d)
|
|
||||||
c_d.valid := in.c.valid && in.c.bits.opcode === Release
|
|
||||||
c_d.bits.opcode := ReleaseAck
|
|
||||||
c_d.bits.param := UInt(0)
|
|
||||||
c_d.bits.size := in.c.bits.size
|
|
||||||
c_d.bits.source := in.c.bits.source
|
|
||||||
c_d.bits.sink := UInt(0)
|
|
||||||
c_d.bits.addr_lo := in.c.bits.address
|
|
||||||
c_d.bits.data := UInt(0)
|
|
||||||
c_d.bits.error := Bool(false)
|
|
||||||
|
|
||||||
assert (!in.c.valid || in.c.bits.opcode === Release || in.c.bits.opcode === ReleaseData)
|
|
||||||
in.c.ready := Mux(in.c.bits.opcode === Release, c_d.ready, c_a.ready)
|
|
||||||
|
|
||||||
// Discard E
|
|
||||||
in.e.ready := Bool(true)
|
|
||||||
|
|
||||||
// Block B; should never happen
|
|
||||||
out.b.ready := Bool(false)
|
|
||||||
assert (!out.b.valid)
|
|
||||||
|
|
||||||
// Take responses from D and transform them
|
|
||||||
val d_d = Wire(in.d)
|
|
||||||
d_d <> out.d
|
|
||||||
d_d.bits.source := out.d.bits.source >> 1
|
|
||||||
|
|
||||||
when (out.d.bits.opcode === AccessAckData && out.d.bits.source(0)) {
|
|
||||||
d_d.bits.opcode := GrantData
|
|
||||||
d_d.bits.param := TLPermissions.toT
|
|
||||||
}
|
|
||||||
when (out.d.bits.opcode === AccessAck && !out.d.bits.source(0)) {
|
|
||||||
d_d.bits.opcode := ReleaseAck
|
|
||||||
}
|
|
||||||
|
|
||||||
// Combine the sources of messages into the channels
|
|
||||||
TLArbiter(TLArbiter.lowestIndexFirst)(out.a, (edgeOut.numBeats1(c_a.bits), c_a), (edgeOut.numBeats1(a_a.bits), a_a))
|
|
||||||
TLArbiter(TLArbiter.lowestIndexFirst)(in.d, (edgeIn .numBeats1(d_d.bits), d_d), (UInt(0), Queue(c_d, 2)))
|
|
||||||
|
|
||||||
// Tie off unused ports
|
|
||||||
in.b.valid := Bool(false)
|
|
||||||
out.c.valid := Bool(false)
|
|
||||||
out.e.valid := Bool(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,8 +11,8 @@ import scala.math.{min,max}
|
|||||||
class TLFilter(select: AddressSet)(implicit p: Parameters) extends LazyModule
|
class TLFilter(select: AddressSet)(implicit p: Parameters) extends LazyModule
|
||||||
{
|
{
|
||||||
val node = TLAdapterNode(
|
val node = TLAdapterNode(
|
||||||
clientFn = { case Seq(cp) => cp },
|
clientFn = { cp => cp },
|
||||||
managerFn = { case Seq(mp) =>
|
managerFn = { mp =>
|
||||||
mp.copy(managers = mp.managers.map { m =>
|
mp.copy(managers = mp.managers.map { m =>
|
||||||
val filtered = m.address.map(_.intersect(select)).flatten
|
val filtered = m.address.map(_.intersect(select)).flatten
|
||||||
val alignment = select.alignment /* alignment 0 means 'select' selected everything */
|
val alignment = select.alignment /* alignment 0 means 'select' selected everything */
|
||||||
|
@ -41,8 +41,8 @@ class TLFragmenter(val minSize: Int, val maxSize: Int, val alwaysMin: Boolean =
|
|||||||
sourceId = IdRange(c.sourceId.start << fragmentBits, c.sourceId.end << fragmentBits))
|
sourceId = IdRange(c.sourceId.start << fragmentBits, c.sourceId.end << fragmentBits))
|
||||||
|
|
||||||
val node = TLAdapterNode(
|
val node = TLAdapterNode(
|
||||||
clientFn = { case Seq(c) => c.copy(clients = c.clients.map(mapClient)) },
|
clientFn = { c => c.copy(clients = c.clients.map(mapClient)) },
|
||||||
managerFn = { case Seq(m) => m.copy(managers = m.managers.map(mapManager)) })
|
managerFn = { m => m.copy(managers = m.managers.map(mapManager)) })
|
||||||
|
|
||||||
lazy val module = new LazyModuleImp(this) {
|
lazy val module = new LazyModuleImp(this) {
|
||||||
val io = new Bundle {
|
val io = new Bundle {
|
||||||
@ -50,204 +50,201 @@ class TLFragmenter(val minSize: Int, val maxSize: Int, val alwaysMin: Boolean =
|
|||||||
val out = node.bundleOut
|
val out = node.bundleOut
|
||||||
}
|
}
|
||||||
|
|
||||||
// All managers must share a common FIFO domain (responses might end up interleaved)
|
((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
|
||||||
val edgeOut = node.edgesOut(0)
|
// All managers must share a common FIFO domain (responses might end up interleaved)
|
||||||
val edgeIn = node.edgesIn(0)
|
val manager = edgeOut.manager
|
||||||
val manager = edgeOut.manager
|
val managers = manager.managers
|
||||||
val managers = manager.managers
|
val beatBytes = manager.beatBytes
|
||||||
val beatBytes = manager.beatBytes
|
val fifoId = managers(0).fifoId
|
||||||
val fifoId = managers(0).fifoId
|
require (fifoId.isDefined && managers.map(_.fifoId == fifoId).reduce(_ && _))
|
||||||
require (fifoId.isDefined && managers.map(_.fifoId == fifoId).reduce(_ && _))
|
|
||||||
|
|
||||||
// We don't support fragmenting to sub-beat accesses
|
// We don't support fragmenting to sub-beat accesses
|
||||||
require (minSize >= beatBytes)
|
require (minSize >= beatBytes)
|
||||||
// We can't support devices which are cached on both sides of us
|
// We can't support devices which are cached on both sides of us
|
||||||
require (!edgeOut.manager.anySupportAcquireB || !edgeIn.client.anySupportProbe)
|
require (!edgeOut.manager.anySupportAcquireB || !edgeIn.client.anySupportProbe)
|
||||||
|
|
||||||
/* The Fragmenter is a bit tricky, because there are 5 sizes in play:
|
/* The Fragmenter is a bit tricky, because there are 5 sizes in play:
|
||||||
* max size -- the maximum transfer size possible
|
* max size -- the maximum transfer size possible
|
||||||
* orig size -- the original pre-fragmenter size
|
* orig size -- the original pre-fragmenter size
|
||||||
* frag size -- the modified post-fragmenter size
|
* frag size -- the modified post-fragmenter size
|
||||||
* min size -- the threshold below which frag=orig
|
* min size -- the threshold below which frag=orig
|
||||||
* beat size -- the amount transfered on any given beat
|
* beat size -- the amount transfered on any given beat
|
||||||
*
|
*
|
||||||
* The relationships are as follows:
|
* The relationships are as follows:
|
||||||
* max >= orig >= frag
|
* max >= orig >= frag
|
||||||
* max > min >= beat
|
* max > min >= beat
|
||||||
* It IS possible that orig <= min (then frag=orig; ie: no fragmentation)
|
* It IS possible that orig <= min (then frag=orig; ie: no fragmentation)
|
||||||
*
|
*
|
||||||
* The fragment# (sent via TL.source) is measured in multiples of min size.
|
* The fragment# (sent via TL.source) is measured in multiples of min size.
|
||||||
* Meanwhile, to track the progress, counters measure in multiples of beat size.
|
* Meanwhile, to track the progress, counters measure in multiples of beat size.
|
||||||
*
|
*
|
||||||
* Here is an example of a bus with max=256, min=8, beat=4 and a device supporting 16.
|
* Here is an example of a bus with max=256, min=8, beat=4 and a device supporting 16.
|
||||||
*
|
*
|
||||||
* in.A out.A (frag#) out.D (frag#) in.D gen# ack#
|
* in.A out.A (frag#) out.D (frag#) in.D gen# ack#
|
||||||
* get64 get16 6 ackD16 6 ackD64 12 15
|
* get64 get16 6 ackD16 6 ackD64 12 15
|
||||||
* ackD16 6 ackD64 14
|
* ackD16 6 ackD64 14
|
||||||
* ackD16 6 ackD64 13
|
* ackD16 6 ackD64 13
|
||||||
* ackD16 6 ackD64 12
|
* ackD16 6 ackD64 12
|
||||||
* get16 4 ackD16 4 ackD64 8 11
|
* get16 4 ackD16 4 ackD64 8 11
|
||||||
* ackD16 4 ackD64 10
|
* ackD16 4 ackD64 10
|
||||||
* ackD16 4 ackD64 9
|
* ackD16 4 ackD64 9
|
||||||
* ackD16 4 ackD64 8
|
* ackD16 4 ackD64 8
|
||||||
* get16 2 ackD16 2 ackD64 4 7
|
* get16 2 ackD16 2 ackD64 4 7
|
||||||
* ackD16 2 ackD64 6
|
* ackD16 2 ackD64 6
|
||||||
* ackD16 2 ackD64 5
|
* ackD16 2 ackD64 5
|
||||||
* ackD16 2 ackD64 4
|
* ackD16 2 ackD64 4
|
||||||
* get16 0 ackD16 0 ackD64 0 3
|
* get16 0 ackD16 0 ackD64 0 3
|
||||||
* ackD16 0 ackD64 2
|
* ackD16 0 ackD64 2
|
||||||
* ackD16 0 ackD64 1
|
* ackD16 0 ackD64 1
|
||||||
* ackD16 0 ackD64 0
|
* ackD16 0 ackD64 0
|
||||||
*
|
*
|
||||||
* get8 get8 0 ackD8 0 ackD8 0 1
|
* get8 get8 0 ackD8 0 ackD8 0 1
|
||||||
* ackD8 0 ackD8 0
|
* ackD8 0 ackD8 0
|
||||||
*
|
*
|
||||||
* get4 get4 0 ackD4 0 ackD4 0 0
|
* get4 get4 0 ackD4 0 ackD4 0 0
|
||||||
* get1 get1 0 ackD1 0 ackD1 0 0
|
* get1 get1 0 ackD1 0 ackD1 0 0
|
||||||
*
|
*
|
||||||
* put64 put16 6 15
|
* put64 put16 6 15
|
||||||
* put64 put16 6 14
|
* put64 put16 6 14
|
||||||
* put64 put16 6 13
|
* put64 put16 6 13
|
||||||
* put64 put16 6 ack16 6 12 12
|
* put64 put16 6 ack16 6 12 12
|
||||||
* put64 put16 4 11
|
* put64 put16 4 11
|
||||||
* put64 put16 4 10
|
* put64 put16 4 10
|
||||||
* put64 put16 4 9
|
* put64 put16 4 9
|
||||||
* put64 put16 4 ack16 4 8 8
|
* put64 put16 4 ack16 4 8 8
|
||||||
* put64 put16 2 7
|
* put64 put16 2 7
|
||||||
* put64 put16 2 6
|
* put64 put16 2 6
|
||||||
* put64 put16 2 5
|
* put64 put16 2 5
|
||||||
* put64 put16 2 ack16 2 4 4
|
* put64 put16 2 ack16 2 4 4
|
||||||
* put64 put16 0 3
|
* put64 put16 0 3
|
||||||
* put64 put16 0 2
|
* put64 put16 0 2
|
||||||
* put64 put16 0 1
|
* put64 put16 0 1
|
||||||
* put64 put16 0 ack16 0 ack64 0 0
|
* put64 put16 0 ack16 0 ack64 0 0
|
||||||
*
|
*
|
||||||
* put8 put8 0 1
|
* put8 put8 0 1
|
||||||
* put8 put8 0 ack8 0 ack8 0 0
|
* put8 put8 0 ack8 0 ack8 0 0
|
||||||
*
|
*
|
||||||
* put4 put4 0 ack4 0 ack4 0 0
|
* put4 put4 0 ack4 0 ack4 0 0
|
||||||
* put1 put1 0 ack1 0 ack1 0 0
|
* put1 put1 0 ack1 0 ack1 0 0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
val in = io.in(0)
|
val counterBits = log2Up(maxSize/beatBytes)
|
||||||
val out = io.out(0)
|
val maxDownSize = if (alwaysMin) minSize else manager.maxTransfer
|
||||||
|
|
||||||
val counterBits = log2Up(maxSize/beatBytes)
|
// First, handle the return path
|
||||||
val maxDownSize = if (alwaysMin) minSize else manager.maxTransfer
|
val acknum = RegInit(UInt(0, width = counterBits))
|
||||||
|
val dOrig = Reg(UInt())
|
||||||
|
val dFragnum = out.d.bits.source(fragmentBits-1, 0)
|
||||||
|
val dFirst = acknum === UInt(0)
|
||||||
|
val dsizeOH = UIntToOH (out.d.bits.size, log2Ceil(maxDownSize)+1)
|
||||||
|
val dsizeOH1 = UIntToOH1(out.d.bits.size, log2Up(maxDownSize))
|
||||||
|
val dHasData = edgeOut.hasData(out.d.bits)
|
||||||
|
|
||||||
// First, handle the return path
|
// calculate new acknum
|
||||||
val acknum = RegInit(UInt(0, width = counterBits))
|
val acknum_fragment = dFragnum << log2Ceil(minSize/beatBytes)
|
||||||
val dOrig = Reg(UInt())
|
val acknum_size = dsizeOH1 >> log2Ceil(beatBytes)
|
||||||
val dFragnum = out.d.bits.source(fragmentBits-1, 0)
|
assert (!out.d.valid || (acknum_fragment & acknum_size) === UInt(0))
|
||||||
val dFirst = acknum === UInt(0)
|
val dFirst_acknum = acknum_fragment | Mux(dHasData, acknum_size, UInt(0))
|
||||||
val dsizeOH = UIntToOH (out.d.bits.size, log2Ceil(maxDownSize)+1)
|
val ack_decrement = Mux(dHasData, UInt(1), dsizeOH >> log2Ceil(beatBytes))
|
||||||
val dsizeOH1 = UIntToOH1(out.d.bits.size, log2Up(maxDownSize))
|
// calculate the original size
|
||||||
val dHasData = edgeOut.hasData(out.d.bits)
|
val dFirst_size = OH1ToUInt((dFragnum << log2Ceil(minSize)) | dsizeOH1)
|
||||||
|
|
||||||
// calculate new acknum
|
when (out.d.fire()) {
|
||||||
val acknum_fragment = dFragnum << log2Ceil(minSize/beatBytes)
|
acknum := Mux(dFirst, dFirst_acknum, acknum - ack_decrement)
|
||||||
val acknum_size = dsizeOH1 >> log2Ceil(beatBytes)
|
when (dFirst) { dOrig := dFirst_size }
|
||||||
assert (!out.d.valid || (acknum_fragment & acknum_size) === UInt(0))
|
}
|
||||||
val dFirst_acknum = acknum_fragment | Mux(dHasData, acknum_size, UInt(0))
|
|
||||||
val ack_decrement = Mux(dHasData, UInt(1), dsizeOH >> log2Ceil(beatBytes))
|
|
||||||
// calculate the original size
|
|
||||||
val dFirst_size = OH1ToUInt((dFragnum << log2Ceil(minSize)) | dsizeOH1)
|
|
||||||
|
|
||||||
when (out.d.fire()) {
|
// Swallow up non-data ack fragments
|
||||||
acknum := Mux(dFirst, dFirst_acknum, acknum - ack_decrement)
|
val drop = !dHasData && (dFragnum =/= UInt(0))
|
||||||
when (dFirst) { dOrig := dFirst_size }
|
out.d.ready := in.d.ready || drop
|
||||||
|
in.d.valid := out.d.valid && !drop
|
||||||
|
in.d.bits := out.d.bits // pass most stuff unchanged
|
||||||
|
in.d.bits.addr_lo := out.d.bits.addr_lo & ~dsizeOH1
|
||||||
|
in.d.bits.source := out.d.bits.source >> fragmentBits
|
||||||
|
in.d.bits.size := Mux(dFirst, dFirst_size, dOrig)
|
||||||
|
|
||||||
|
// Combine the error flag
|
||||||
|
val r_error = RegInit(Bool(false))
|
||||||
|
val d_error = r_error | out.d.bits.error
|
||||||
|
when (out.d.fire()) { r_error := Mux(drop, d_error, UInt(0)) }
|
||||||
|
in.d.bits.error := d_error
|
||||||
|
|
||||||
|
// What maximum transfer sizes do downstream devices support?
|
||||||
|
val maxArithmetics = managers.map(_.supportsArithmetic.max)
|
||||||
|
val maxLogicals = managers.map(_.supportsLogical.max)
|
||||||
|
val maxGets = managers.map(_.supportsGet.max)
|
||||||
|
val maxPutFulls = managers.map(_.supportsPutFull.max)
|
||||||
|
val maxPutPartials = managers.map(_.supportsPutPartial.max)
|
||||||
|
val maxHints = managers.map(m => if (m.supportsHint) maxDownSize else 0)
|
||||||
|
|
||||||
|
// We assume that the request is valid => size 0 is impossible
|
||||||
|
val lgMinSize = UInt(log2Ceil(minSize))
|
||||||
|
val maxLgArithmetics = maxArithmetics.map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m)))
|
||||||
|
val maxLgLogicals = maxLogicals .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m)))
|
||||||
|
val maxLgGets = maxGets .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m)))
|
||||||
|
val maxLgPutFulls = maxPutFulls .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m)))
|
||||||
|
val maxLgPutPartials = maxPutPartials.map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m)))
|
||||||
|
val maxLgHints = maxHints .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m)))
|
||||||
|
|
||||||
|
// Make the request repeatable
|
||||||
|
val repeater = Module(new Repeater(in.a.bits))
|
||||||
|
repeater.io.enq <> in.a
|
||||||
|
val in_a = repeater.io.deq
|
||||||
|
|
||||||
|
// If this is infront of a single manager, these become constants
|
||||||
|
val find = manager.findFast(edgeIn.address(in_a.bits))
|
||||||
|
val maxLgArithmetic = Mux1H(find, maxLgArithmetics)
|
||||||
|
val maxLgLogical = Mux1H(find, maxLgLogicals)
|
||||||
|
val maxLgGet = Mux1H(find, maxLgGets)
|
||||||
|
val maxLgPutFull = Mux1H(find, maxLgPutFulls)
|
||||||
|
val maxLgPutPartial = Mux1H(find, maxLgPutPartials)
|
||||||
|
val maxLgHint = Mux1H(find, maxLgHints)
|
||||||
|
|
||||||
|
val limit = if (alwaysMin) lgMinSize else
|
||||||
|
MuxLookup(in_a.bits.opcode, lgMinSize, Array(
|
||||||
|
TLMessages.PutFullData -> maxLgPutFull,
|
||||||
|
TLMessages.PutPartialData -> maxLgPutPartial,
|
||||||
|
TLMessages.ArithmeticData -> maxLgArithmetic,
|
||||||
|
TLMessages.LogicalData -> maxLgLogical,
|
||||||
|
TLMessages.Get -> maxLgGet,
|
||||||
|
TLMessages.Hint -> maxLgHint))
|
||||||
|
|
||||||
|
val aOrig = in_a.bits.size
|
||||||
|
val aFrag = Mux(aOrig > limit, limit, aOrig)
|
||||||
|
val aOrigOH1 = UIntToOH1(aOrig, log2Ceil(maxSize))
|
||||||
|
val aFragOH1 = UIntToOH1(aFrag, log2Up(maxDownSize))
|
||||||
|
val aHasData = node.edgesIn(0).hasData(in_a.bits)
|
||||||
|
val aMask = Mux(aHasData, UInt(0), aFragOH1)
|
||||||
|
|
||||||
|
val gennum = RegInit(UInt(0, width = counterBits))
|
||||||
|
val aFirst = gennum === UInt(0)
|
||||||
|
val old_gennum1 = Mux(aFirst, aOrigOH1 >> log2Ceil(beatBytes), gennum - UInt(1))
|
||||||
|
val new_gennum = ~(~old_gennum1 | (aMask >> log2Ceil(beatBytes))) // ~(~x|y) is width safe
|
||||||
|
val aFragnum = ~(~(old_gennum1 >> log2Ceil(minSize/beatBytes)) | (aFragOH1 >> log2Ceil(minSize)))
|
||||||
|
|
||||||
|
when (out.a.fire()) { gennum := new_gennum }
|
||||||
|
|
||||||
|
repeater.io.repeat := !aHasData && aFragnum =/= UInt(0)
|
||||||
|
out.a <> in_a
|
||||||
|
out.a.bits.address := in_a.bits.address | (~aFragnum << log2Ceil(minSize) & aOrigOH1)
|
||||||
|
out.a.bits.source := Cat(in_a.bits.source, aFragnum)
|
||||||
|
out.a.bits.size := aFrag
|
||||||
|
|
||||||
|
// Optimize away some of the Repeater's registers
|
||||||
|
assert (!repeater.io.full || !aHasData)
|
||||||
|
out.a.bits.data := in.a.bits.data
|
||||||
|
val fullMask = UInt((BigInt(1) << beatBytes) - 1)
|
||||||
|
assert (!repeater.io.full || in_a.bits.mask === fullMask)
|
||||||
|
out.a.bits.mask := Mux(repeater.io.full, fullMask, in.a.bits.mask)
|
||||||
|
|
||||||
|
// Tie off unused channels
|
||||||
|
in.b.valid := Bool(false)
|
||||||
|
in.c.ready := Bool(true)
|
||||||
|
in.e.ready := Bool(true)
|
||||||
|
out.b.ready := Bool(true)
|
||||||
|
out.c.valid := Bool(false)
|
||||||
|
out.e.valid := Bool(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Swallow up non-data ack fragments
|
|
||||||
val drop = !dHasData && (dFragnum =/= UInt(0))
|
|
||||||
out.d.ready := in.d.ready || drop
|
|
||||||
in.d.valid := out.d.valid && !drop
|
|
||||||
in.d.bits := out.d.bits // pass most stuff unchanged
|
|
||||||
in.d.bits.addr_lo := out.d.bits.addr_lo & ~dsizeOH1
|
|
||||||
in.d.bits.source := out.d.bits.source >> fragmentBits
|
|
||||||
in.d.bits.size := Mux(dFirst, dFirst_size, dOrig)
|
|
||||||
|
|
||||||
// Combine the error flag
|
|
||||||
val r_error = RegInit(Bool(false))
|
|
||||||
val d_error = r_error | out.d.bits.error
|
|
||||||
when (out.d.fire()) { r_error := Mux(drop, d_error, UInt(0)) }
|
|
||||||
in.d.bits.error := d_error
|
|
||||||
|
|
||||||
// What maximum transfer sizes do downstream devices support?
|
|
||||||
val maxArithmetics = managers.map(_.supportsArithmetic.max)
|
|
||||||
val maxLogicals = managers.map(_.supportsLogical.max)
|
|
||||||
val maxGets = managers.map(_.supportsGet.max)
|
|
||||||
val maxPutFulls = managers.map(_.supportsPutFull.max)
|
|
||||||
val maxPutPartials = managers.map(_.supportsPutPartial.max)
|
|
||||||
val maxHints = managers.map(m => if (m.supportsHint) maxDownSize else 0)
|
|
||||||
|
|
||||||
// We assume that the request is valid => size 0 is impossible
|
|
||||||
val lgMinSize = UInt(log2Ceil(minSize))
|
|
||||||
val maxLgArithmetics = maxArithmetics.map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m)))
|
|
||||||
val maxLgLogicals = maxLogicals .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m)))
|
|
||||||
val maxLgGets = maxGets .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m)))
|
|
||||||
val maxLgPutFulls = maxPutFulls .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m)))
|
|
||||||
val maxLgPutPartials = maxPutPartials.map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m)))
|
|
||||||
val maxLgHints = maxHints .map(m => if (m == 0) lgMinSize else UInt(log2Ceil(m)))
|
|
||||||
|
|
||||||
// Make the request repeatable
|
|
||||||
val repeater = Module(new Repeater(in.a.bits))
|
|
||||||
repeater.io.enq <> in.a
|
|
||||||
val in_a = repeater.io.deq
|
|
||||||
|
|
||||||
// If this is infront of a single manager, these become constants
|
|
||||||
val find = manager.findFast(edgeIn.address(in_a.bits))
|
|
||||||
val maxLgArithmetic = Mux1H(find, maxLgArithmetics)
|
|
||||||
val maxLgLogical = Mux1H(find, maxLgLogicals)
|
|
||||||
val maxLgGet = Mux1H(find, maxLgGets)
|
|
||||||
val maxLgPutFull = Mux1H(find, maxLgPutFulls)
|
|
||||||
val maxLgPutPartial = Mux1H(find, maxLgPutPartials)
|
|
||||||
val maxLgHint = Mux1H(find, maxLgHints)
|
|
||||||
|
|
||||||
val limit = if (alwaysMin) lgMinSize else
|
|
||||||
MuxLookup(in_a.bits.opcode, lgMinSize, Array(
|
|
||||||
TLMessages.PutFullData -> maxLgPutFull,
|
|
||||||
TLMessages.PutPartialData -> maxLgPutPartial,
|
|
||||||
TLMessages.ArithmeticData -> maxLgArithmetic,
|
|
||||||
TLMessages.LogicalData -> maxLgLogical,
|
|
||||||
TLMessages.Get -> maxLgGet,
|
|
||||||
TLMessages.Hint -> maxLgHint))
|
|
||||||
|
|
||||||
val aOrig = in_a.bits.size
|
|
||||||
val aFrag = Mux(aOrig > limit, limit, aOrig)
|
|
||||||
val aOrigOH1 = UIntToOH1(aOrig, log2Ceil(maxSize))
|
|
||||||
val aFragOH1 = UIntToOH1(aFrag, log2Up(maxDownSize))
|
|
||||||
val aHasData = node.edgesIn(0).hasData(in_a.bits)
|
|
||||||
val aMask = Mux(aHasData, UInt(0), aFragOH1)
|
|
||||||
|
|
||||||
val gennum = RegInit(UInt(0, width = counterBits))
|
|
||||||
val aFirst = gennum === UInt(0)
|
|
||||||
val old_gennum1 = Mux(aFirst, aOrigOH1 >> log2Ceil(beatBytes), gennum - UInt(1))
|
|
||||||
val new_gennum = ~(~old_gennum1 | (aMask >> log2Ceil(beatBytes))) // ~(~x|y) is width safe
|
|
||||||
val aFragnum = ~(~(old_gennum1 >> log2Ceil(minSize/beatBytes)) | (aFragOH1 >> log2Ceil(minSize)))
|
|
||||||
|
|
||||||
when (out.a.fire()) { gennum := new_gennum }
|
|
||||||
|
|
||||||
repeater.io.repeat := !aHasData && aFragnum =/= UInt(0)
|
|
||||||
out.a <> in_a
|
|
||||||
out.a.bits.address := in_a.bits.address | (~aFragnum << log2Ceil(minSize) & aOrigOH1)
|
|
||||||
out.a.bits.source := Cat(in_a.bits.source, aFragnum)
|
|
||||||
out.a.bits.size := aFrag
|
|
||||||
|
|
||||||
// Optimize away some of the Repeater's registers
|
|
||||||
assert (!repeater.io.full || !aHasData)
|
|
||||||
out.a.bits.data := in.a.bits.data
|
|
||||||
val fullMask = UInt((BigInt(1) << beatBytes) - 1)
|
|
||||||
assert (!repeater.io.full || in_a.bits.mask === fullMask)
|
|
||||||
out.a.bits.mask := Mux(repeater.io.full, fullMask, in.a.bits.mask)
|
|
||||||
|
|
||||||
// Tie off unused channels
|
|
||||||
in.b.valid := Bool(false)
|
|
||||||
in.c.ready := Bool(true)
|
|
||||||
in.e.ready := Bool(true)
|
|
||||||
out.b.ready := Bool(true)
|
|
||||||
out.c.valid := Bool(false)
|
|
||||||
out.e.valid := Bool(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,8 +12,8 @@ import diplomacy._
|
|||||||
class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = false, passthrough: Boolean = true)(implicit p: Parameters) extends LazyModule
|
class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = false, passthrough: Boolean = true)(implicit p: Parameters) extends LazyModule
|
||||||
{
|
{
|
||||||
val node = TLAdapterNode(
|
val node = TLAdapterNode(
|
||||||
clientFn = { case Seq(c) => if (!supportClients) c else c.copy(minLatency = min(1, c.minLatency), clients = c.clients .map(_.copy(supportsHint = TransferSizes(1, c.maxTransfer)))) },
|
clientFn = { c => if (!supportClients) c else c.copy(minLatency = min(1, c.minLatency), clients = c.clients .map(_.copy(supportsHint = TransferSizes(1, c.maxTransfer)))) },
|
||||||
managerFn = { case Seq(m) => if (!supportManagers) m else m.copy(minLatency = min(1, m.minLatency), managers = m.managers.map(_.copy(supportsHint = TransferSizes(1, m.maxTransfer)))) })
|
managerFn = { m => if (!supportManagers) m else m.copy(minLatency = min(1, m.minLatency), managers = m.managers.map(_.copy(supportsHint = TransferSizes(1, m.maxTransfer)))) })
|
||||||
|
|
||||||
lazy val module = new LazyModuleImp(this) {
|
lazy val module = new LazyModuleImp(this) {
|
||||||
val io = new Bundle {
|
val io = new Bundle {
|
||||||
@ -21,79 +21,76 @@ class TLHintHandler(supportManagers: Boolean = true, supportClients: Boolean = f
|
|||||||
val out = node.bundleOut
|
val out = node.bundleOut
|
||||||
}
|
}
|
||||||
|
|
||||||
val in = io.in(0)
|
((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
|
||||||
val out = io.out(0)
|
// Don't add support for clients if there is no BCE channel
|
||||||
val edgeIn = node.edgesIn(0)
|
val bce = edgeOut.manager.anySupportAcquireB && edgeIn.client.anySupportProbe
|
||||||
val edgeOut = node.edgesOut(0)
|
require (!supportClients || bce)
|
||||||
|
|
||||||
// Don't add support for clients if there is no BCE channel
|
// Does it even make sense to add the HintHandler?
|
||||||
val bce = edgeOut.manager.anySupportAcquireB && edgeIn.client.anySupportProbe
|
val smartClients = edgeIn.client.clients.map(_.supportsHint.max == edgeIn.client.maxTransfer).reduce(_&&_)
|
||||||
require (!supportClients || bce)
|
val smartManagers = edgeOut.manager.managers.map(_.supportsHint.max == edgeOut.manager.maxTransfer).reduce(_&&_)
|
||||||
|
|
||||||
// Does it even make sense to add the HintHandler?
|
if (supportManagers && !(passthrough && smartManagers)) {
|
||||||
val smartClients = edgeIn.client.clients.map(_.supportsHint.max == edgeIn.client.maxTransfer).reduce(_&&_)
|
val address = edgeIn.address(in.a.bits)
|
||||||
val smartManagers = edgeOut.manager.managers.map(_.supportsHint.max == edgeOut.manager.maxTransfer).reduce(_&&_)
|
val handleA = if (passthrough) !edgeOut.manager.supportsHintFast(address, edgeIn.size(in.a.bits)) else Bool(true)
|
||||||
|
val hintBitsAtA = handleA && in.a.bits.opcode === TLMessages.Hint
|
||||||
|
val hint = Wire(out.d)
|
||||||
|
|
||||||
if (supportManagers && !(passthrough && smartManagers)) {
|
hint.valid := in.a.valid && hintBitsAtA
|
||||||
val address = edgeIn.address(in.a.bits)
|
out.a.valid := in.a.valid && !hintBitsAtA
|
||||||
val handleA = if (passthrough) !edgeOut.manager.supportsHintFast(address, edgeIn.size(in.a.bits)) else Bool(true)
|
in.a.ready := Mux(hintBitsAtA, hint.ready, out.a.ready)
|
||||||
val hintBitsAtA = handleA && in.a.bits.opcode === TLMessages.Hint
|
|
||||||
val hint = Wire(out.d)
|
|
||||||
|
|
||||||
hint.valid := in.a.valid && hintBitsAtA
|
hint.bits := edgeIn.HintAck(in.a.bits, UInt(0))
|
||||||
out.a.valid := in.a.valid && !hintBitsAtA
|
out.a.bits := in.a.bits
|
||||||
in.a.ready := Mux(hintBitsAtA, hint.ready, out.a.ready)
|
|
||||||
|
|
||||||
hint.bits := edgeIn.HintAck(in.a.bits, UInt(0))
|
TLArbiter(TLArbiter.lowestIndexFirst)(in.d, (edgeOut.numBeats1(out.d.bits), out.d), (UInt(0), Queue(hint, 1)))
|
||||||
out.a.bits := in.a.bits
|
} else {
|
||||||
|
out.a.valid := in.a.valid
|
||||||
|
in.a.ready := out.a.ready
|
||||||
|
out.a.bits := in.a.bits
|
||||||
|
|
||||||
TLArbiter(TLArbiter.lowestIndexFirst)(in.d, (edgeOut.numBeats1(out.d.bits), out.d), (UInt(0), Queue(hint, 1)))
|
in.d.valid := out.d.valid
|
||||||
} else {
|
out.d.ready := in.d.ready
|
||||||
out.a.valid := in.a.valid
|
in.d.bits := out.d.bits
|
||||||
in.a.ready := out.a.ready
|
}
|
||||||
out.a.bits := in.a.bits
|
|
||||||
|
|
||||||
in.d.valid := out.d.valid
|
if (supportClients && !(passthrough && smartClients)) {
|
||||||
out.d.ready := in.d.ready
|
val handleB = if (passthrough) !edgeIn.client.supportsHint(out.b.bits.source, edgeOut.size(out.b.bits)) else Bool(true)
|
||||||
in.d.bits := out.d.bits
|
val hintBitsAtB = handleB && out.b.bits.opcode === TLMessages.Hint
|
||||||
}
|
val hint = Wire(in.c)
|
||||||
|
|
||||||
if (supportClients && !(passthrough && smartClients)) {
|
hint.valid := out.b.valid && hintBitsAtB
|
||||||
val handleB = if (passthrough) !edgeIn.client.supportsHint(out.b.bits.source, edgeOut.size(out.b.bits)) else Bool(true)
|
in.b.valid := out.b.valid && !hintBitsAtB
|
||||||
val hintBitsAtB = handleB && out.b.bits.opcode === TLMessages.Hint
|
out.b.ready := Mux(hintBitsAtB, hint.ready, in.b.ready)
|
||||||
val hint = Wire(in.c)
|
|
||||||
|
|
||||||
hint.valid := out.b.valid && hintBitsAtB
|
hint.bits := edgeOut.HintAck(out.b.bits)
|
||||||
in.b.valid := out.b.valid && !hintBitsAtB
|
in.b.bits := out.b.bits
|
||||||
out.b.ready := Mux(hintBitsAtB, hint.ready, in.b.ready)
|
|
||||||
|
|
||||||
hint.bits := edgeOut.HintAck(out.b.bits)
|
TLArbiter(TLArbiter.lowestIndexFirst)(out.c, (edgeIn.numBeats1(in.c.bits), in.c), (UInt(0), Queue(hint, 1)))
|
||||||
in.b.bits := out.b.bits
|
} else if (bce) {
|
||||||
|
in.b.valid := out.b.valid
|
||||||
|
out.b.ready := in.b.ready
|
||||||
|
in.b.bits := out.b.bits
|
||||||
|
|
||||||
TLArbiter(TLArbiter.lowestIndexFirst)(out.c, (edgeIn.numBeats1(in.c.bits), in.c), (UInt(0), Queue(hint, 1)))
|
out.c.valid := in.c.valid
|
||||||
} else if (bce) {
|
in.c.ready := out.c.ready
|
||||||
in.b.valid := out.b.valid
|
out.c.bits := in.c.bits
|
||||||
out.b.ready := in.b.ready
|
} else {
|
||||||
in.b.bits := out.b.bits
|
in.b.valid := Bool(false)
|
||||||
|
in.c.ready := Bool(true)
|
||||||
|
out.b.ready := Bool(true)
|
||||||
|
out.c.valid := Bool(false)
|
||||||
|
}
|
||||||
|
|
||||||
out.c.valid := in.c.valid
|
if (bce) {
|
||||||
in.c.ready := out.c.ready
|
// Pass E through unchanged
|
||||||
out.c.bits := in.c.bits
|
out.e.valid := in.e.valid
|
||||||
} else {
|
in.e.ready := out.e.ready
|
||||||
in.b.valid := Bool(false)
|
out.e.bits := in.e.bits
|
||||||
in.c.ready := Bool(true)
|
} else {
|
||||||
out.b.ready := Bool(true)
|
in.e.ready := Bool(true)
|
||||||
out.c.valid := Bool(false)
|
out.e.valid := Bool(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bce) {
|
|
||||||
// Pass E through unchanged
|
|
||||||
out.e.valid := in.e.valid
|
|
||||||
in.e.ready := out.e.ready
|
|
||||||
out.e.bits := in.e.bits
|
|
||||||
} else {
|
|
||||||
in.e.ready := Bool(true)
|
|
||||||
out.e.valid := Bool(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,16 +81,16 @@ object IntImp extends NodeImp[IntSourcePortParameters, IntSinkPortParameters, In
|
|||||||
|
|
||||||
case class IntIdentityNode() extends IdentityNode(IntImp)
|
case class IntIdentityNode() extends IdentityNode(IntImp)
|
||||||
case class IntSourceNode(num: Int) extends SourceNode(IntImp)(
|
case class IntSourceNode(num: Int) extends SourceNode(IntImp)(
|
||||||
IntSourcePortParameters(Seq(IntSourceParameters(num))), (if (num == 0) 0 else 1) to 1)
|
if (num == 0) Seq() else Seq(IntSourcePortParameters(Seq(IntSourceParameters(num)))))
|
||||||
case class IntSinkNode() extends SinkNode(IntImp)(
|
case class IntSinkNode() extends SinkNode(IntImp)(
|
||||||
IntSinkPortParameters(Seq(IntSinkParameters())))
|
Seq(IntSinkPortParameters(Seq(IntSinkParameters()))))
|
||||||
|
|
||||||
case class IntAdapterNode(
|
case class IntNexusNode(
|
||||||
sourceFn: Seq[IntSourcePortParameters] => IntSourcePortParameters,
|
sourceFn: Seq[IntSourcePortParameters] => IntSourcePortParameters,
|
||||||
sinkFn: Seq[IntSinkPortParameters] => IntSinkPortParameters,
|
sinkFn: Seq[IntSinkPortParameters] => IntSinkPortParameters,
|
||||||
numSourcePorts: Range.Inclusive = 1 to 1,
|
numSourcePorts: Range.Inclusive = 0 to 128,
|
||||||
numSinkPorts: Range.Inclusive = 1 to 1)
|
numSinkPorts: Range.Inclusive = 0 to 128)
|
||||||
extends InteriorNode(IntImp)(sourceFn, sinkFn, numSourcePorts, numSinkPorts)
|
extends NexusNode(IntImp)(sourceFn, sinkFn, numSourcePorts, numSinkPorts)
|
||||||
|
|
||||||
case class IntOutputNode() extends OutputNode(IntImp)
|
case class IntOutputNode() extends OutputNode(IntImp)
|
||||||
case class IntInputNode() extends InputNode(IntImp)
|
case class IntInputNode() extends InputNode(IntImp)
|
||||||
@ -103,9 +103,7 @@ case class IntInternalInputNode(num: Int) extends InternalInputNode(IntImp)(Seq(
|
|||||||
|
|
||||||
class IntXbar()(implicit p: Parameters) extends LazyModule
|
class IntXbar()(implicit p: Parameters) extends LazyModule
|
||||||
{
|
{
|
||||||
val intnode = IntAdapterNode(
|
val intnode = IntNexusNode(
|
||||||
numSourcePorts = 0 to 128,
|
|
||||||
numSinkPorts = 0 to 128,
|
|
||||||
sinkFn = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) },
|
sinkFn = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) },
|
||||||
sourceFn = { seq =>
|
sourceFn = { seq =>
|
||||||
IntSourcePortParameters((seq zip seq.map(_.num).scanLeft(0)(_+_).init).map {
|
IntSourcePortParameters((seq zip seq.map(_.num).scanLeft(0)(_+_).init).map {
|
||||||
|
@ -7,7 +7,7 @@ import chisel3.internal.sourceinfo.{SourceInfo, SourceLine}
|
|||||||
import config._
|
import config._
|
||||||
import diplomacy._
|
import diplomacy._
|
||||||
|
|
||||||
case class TLMonitorArgs(gen: () => TLBundleSnoop, edge: () => TLEdge, sourceInfo: SourceInfo, p: Parameters)
|
case class TLMonitorArgs(edge: () => Seq[TLEdge], sourceInfo: SourceInfo, p: Parameters)
|
||||||
|
|
||||||
abstract class TLMonitorBase(args: TLMonitorArgs) extends LazyModule()(args.p)
|
abstract class TLMonitorBase(args: TLMonitorArgs) extends LazyModule()(args.p)
|
||||||
{
|
{
|
||||||
@ -16,11 +16,12 @@ abstract class TLMonitorBase(args: TLMonitorArgs) extends LazyModule()(args.p)
|
|||||||
def legalize(bundle: TLBundleSnoop, edge: TLEdge, reset: Bool): Unit
|
def legalize(bundle: TLBundleSnoop, edge: TLEdge, reset: Bool): Unit
|
||||||
|
|
||||||
lazy val module = new LazyModuleImp(this) {
|
lazy val module = new LazyModuleImp(this) {
|
||||||
|
val edges = args.edge()
|
||||||
val io = new Bundle {
|
val io = new Bundle {
|
||||||
val in = args.gen().asInput
|
val in = Vec(edges.size, new TLBundleSnoop(TLBundleParameters.union(edges.map(_.bundle)))).flip
|
||||||
}
|
}
|
||||||
|
|
||||||
legalize(io.in, args.edge(), reset)
|
(edges zip io.in).foreach { case (e, in) => legalize(in, e, reset) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,44 +24,47 @@ object TLImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TL
|
|||||||
override def labelI(ei: TLEdgeIn) = (ei.manager.beatBytes * 8).toString
|
override def labelI(ei: TLEdgeIn) = (ei.manager.beatBytes * 8).toString
|
||||||
override def labelO(eo: TLEdgeOut) = (eo.manager.beatBytes * 8).toString
|
override def labelO(eo: TLEdgeOut) = (eo.manager.beatBytes * 8).toString
|
||||||
|
|
||||||
def connect(bo: => TLBundle, bi: => TLBundle, ei: => TLEdgeIn)(implicit p: Parameters, sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = {
|
override def connect(bindings: () => Seq[(TLEdgeIn, TLBundle, TLBundle)])(implicit p: Parameters, sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = {
|
||||||
val monitor = p(TLMonitorBuilder)(TLMonitorArgs(() => new TLBundleSnoop(bo.params), () => ei, sourceInfo, p))
|
val monitor = p(TLMonitorBuilder)(TLMonitorArgs(() => bindings().map(_._1), sourceInfo, p))
|
||||||
(monitor, () => {
|
(monitor, () => {
|
||||||
bi <> bo
|
val eval = bindings ()
|
||||||
monitor.foreach { _.module.io.in := TLBundleSnoop(bo) }
|
monitor.foreach { m => (eval zip m.module.io.in) foreach { case ((_,i,o), m) => m := TLBundleSnoop(o,i) } }
|
||||||
if (p(TLCombinationalCheck)) {
|
eval.foreach { case (_, bi, bo) =>
|
||||||
// It is forbidden for valid to depend on ready in TL2
|
bi <> bo
|
||||||
// If someone did that, then this will create a detectable combinational loop
|
if (p(TLCombinationalCheck)) {
|
||||||
bo.a.ready := bi.a.ready && bo.a.valid
|
// It is forbidden for valid to depend on ready in TL2
|
||||||
bi.b.ready := bo.b.ready && bi.b.valid
|
// If someone did that, then this will create a detectable combinational loop
|
||||||
bo.c.ready := bi.c.ready && bo.c.valid
|
bo.a.ready := bi.a.ready && bo.a.valid
|
||||||
bi.d.ready := bo.d.ready && bi.d.valid
|
bi.b.ready := bo.b.ready && bi.b.valid
|
||||||
bo.e.ready := bi.e.ready && bo.e.valid
|
bo.c.ready := bi.c.ready && bo.c.valid
|
||||||
}
|
bi.d.ready := bo.d.ready && bi.d.valid
|
||||||
if (p(TLCombinationalCheck)) {
|
bo.e.ready := bi.e.ready && bo.e.valid
|
||||||
// Randomly stall the transfers
|
}
|
||||||
val allow = LFSRNoiseMaker(5)
|
if (p(TLCombinationalCheck)) {
|
||||||
bi.a.valid := bo.a.valid && allow(0)
|
// Randomly stall the transfers
|
||||||
bo.a.ready := bi.a.ready && allow(0)
|
val allow = LFSRNoiseMaker(5)
|
||||||
bo.b.valid := bi.b.valid && allow(1)
|
bi.a.valid := bo.a.valid && allow(0)
|
||||||
bi.b.ready := bo.b.ready && allow(1)
|
bo.a.ready := bi.a.ready && allow(0)
|
||||||
bi.c.valid := bo.c.valid && allow(2)
|
bo.b.valid := bi.b.valid && allow(1)
|
||||||
bo.c.ready := bi.c.ready && allow(2)
|
bi.b.ready := bo.b.ready && allow(1)
|
||||||
bo.d.valid := bi.d.valid && allow(3)
|
bi.c.valid := bo.c.valid && allow(2)
|
||||||
bi.d.ready := bo.d.ready && allow(3)
|
bo.c.ready := bi.c.ready && allow(2)
|
||||||
bi.e.valid := bo.e.valid && allow(4)
|
bo.d.valid := bi.d.valid && allow(3)
|
||||||
bo.e.ready := bi.e.ready && allow(4)
|
bi.d.ready := bo.d.ready && allow(3)
|
||||||
// Inject garbage whenever not valid
|
bi.e.valid := bo.e.valid && allow(4)
|
||||||
val bits_a = bo.a.bits.fromBits(LFSRNoiseMaker(bo.a.bits.asUInt.getWidth))
|
bo.e.ready := bi.e.ready && allow(4)
|
||||||
val bits_b = bi.b.bits.fromBits(LFSRNoiseMaker(bi.b.bits.asUInt.getWidth))
|
// Inject garbage whenever not valid
|
||||||
val bits_c = bo.c.bits.fromBits(LFSRNoiseMaker(bo.c.bits.asUInt.getWidth))
|
val bits_a = bo.a.bits.fromBits(LFSRNoiseMaker(bo.a.bits.asUInt.getWidth))
|
||||||
val bits_d = bi.d.bits.fromBits(LFSRNoiseMaker(bi.d.bits.asUInt.getWidth))
|
val bits_b = bi.b.bits.fromBits(LFSRNoiseMaker(bi.b.bits.asUInt.getWidth))
|
||||||
val bits_e = bo.e.bits.fromBits(LFSRNoiseMaker(bo.e.bits.asUInt.getWidth))
|
val bits_c = bo.c.bits.fromBits(LFSRNoiseMaker(bo.c.bits.asUInt.getWidth))
|
||||||
when (!bi.a.valid) { bi.a.bits := bits_a }
|
val bits_d = bi.d.bits.fromBits(LFSRNoiseMaker(bi.d.bits.asUInt.getWidth))
|
||||||
when (!bo.b.valid) { bo.b.bits := bits_b }
|
val bits_e = bo.e.bits.fromBits(LFSRNoiseMaker(bo.e.bits.asUInt.getWidth))
|
||||||
when (!bi.c.valid) { bi.c.bits := bits_c }
|
when (!bi.a.valid) { bi.a.bits := bits_a }
|
||||||
when (!bo.d.valid) { bo.d.bits := bits_d }
|
when (!bo.b.valid) { bo.b.bits := bits_b }
|
||||||
when (!bi.e.valid) { bi.e.bits := bits_e }
|
when (!bi.c.valid) { bi.c.bits := bits_c }
|
||||||
|
when (!bo.d.valid) { bo.d.bits := bits_d }
|
||||||
|
when (!bi.e.valid) { bi.e.bits := bits_e }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -86,29 +89,33 @@ object TLImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TL
|
|||||||
|
|
||||||
// Nodes implemented inside modules
|
// Nodes implemented inside modules
|
||||||
case class TLIdentityNode() extends IdentityNode(TLImp)
|
case class TLIdentityNode() extends IdentityNode(TLImp)
|
||||||
case class TLClientNode(portParams: TLClientPortParameters, numPorts: Range.Inclusive = 1 to 1)
|
case class TLClientNode(portParams: Seq[TLClientPortParameters]) extends SourceNode(TLImp)(portParams)
|
||||||
extends SourceNode(TLImp)(portParams, numPorts)
|
case class TLManagerNode(portParams: Seq[TLManagerPortParameters]) extends SinkNode(TLImp)(portParams)
|
||||||
case class TLManagerNode(portParams: TLManagerPortParameters, numPorts: Range.Inclusive = 1 to 1)
|
|
||||||
extends SinkNode(TLImp)(portParams, numPorts)
|
|
||||||
|
|
||||||
object TLClientNode
|
object TLClientNode
|
||||||
{
|
{
|
||||||
def apply(params: TLClientParameters) =
|
def apply(params: TLClientParameters) =
|
||||||
new TLClientNode(TLClientPortParameters(Seq(params)), 1 to 1)
|
new TLClientNode(Seq(TLClientPortParameters(Seq(params))))
|
||||||
}
|
}
|
||||||
|
|
||||||
object TLManagerNode
|
object TLManagerNode
|
||||||
{
|
{
|
||||||
def apply(beatBytes: Int, params: TLManagerParameters) =
|
def apply(beatBytes: Int, params: TLManagerParameters) =
|
||||||
new TLManagerNode(TLManagerPortParameters(Seq(params), beatBytes, minLatency = 0), 1 to 1)
|
new TLManagerNode(Seq(TLManagerPortParameters(Seq(params), beatBytes, minLatency = 0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
case class TLAdapterNode(
|
case class TLAdapterNode(
|
||||||
|
clientFn: TLClientPortParameters => TLClientPortParameters,
|
||||||
|
managerFn: TLManagerPortParameters => TLManagerPortParameters,
|
||||||
|
num: Range.Inclusive = 0 to 999)
|
||||||
|
extends AdapterNode(TLImp)(clientFn, managerFn, num)
|
||||||
|
|
||||||
|
case class TLNexusNode(
|
||||||
clientFn: Seq[TLClientPortParameters] => TLClientPortParameters,
|
clientFn: Seq[TLClientPortParameters] => TLClientPortParameters,
|
||||||
managerFn: Seq[TLManagerPortParameters] => TLManagerPortParameters,
|
managerFn: Seq[TLManagerPortParameters] => TLManagerPortParameters,
|
||||||
numClientPorts: Range.Inclusive = 1 to 1,
|
numClientPorts: Range.Inclusive = 1 to 999,
|
||||||
numManagerPorts: Range.Inclusive = 1 to 1)
|
numManagerPorts: Range.Inclusive = 1 to 999)
|
||||||
extends InteriorNode(TLImp)(clientFn, managerFn, numClientPorts, numManagerPorts)
|
extends NexusNode(TLImp)(clientFn, managerFn, numClientPorts, numManagerPorts)
|
||||||
|
|
||||||
// Nodes passed from an inner module
|
// Nodes passed from an inner module
|
||||||
case class TLOutputNode() extends OutputNode(TLImp)
|
case class TLOutputNode() extends OutputNode(TLImp)
|
||||||
@ -155,10 +162,6 @@ object TLAsyncImp extends NodeImp[TLAsyncClientPortParameters, TLAsyncManagerPor
|
|||||||
override def labelI(ei: TLAsyncEdgeParameters) = ei.manager.depth.toString
|
override def labelI(ei: TLAsyncEdgeParameters) = ei.manager.depth.toString
|
||||||
override def labelO(eo: TLAsyncEdgeParameters) = eo.manager.depth.toString
|
override def labelO(eo: TLAsyncEdgeParameters) = eo.manager.depth.toString
|
||||||
|
|
||||||
def connect(bo: => TLAsyncBundle, bi: => TLAsyncBundle, ei: => TLAsyncEdgeParameters)(implicit p: Parameters, sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = {
|
|
||||||
(None, () => { bi <> bo })
|
|
||||||
}
|
|
||||||
|
|
||||||
override def mixO(pd: TLAsyncClientPortParameters, node: OutwardNode[TLAsyncClientPortParameters, TLAsyncManagerPortParameters, TLAsyncBundle]): TLAsyncClientPortParameters =
|
override def mixO(pd: TLAsyncClientPortParameters, node: OutwardNode[TLAsyncClientPortParameters, TLAsyncManagerPortParameters, TLAsyncBundle]): TLAsyncClientPortParameters =
|
||||||
pd.copy(base = pd.base.copy(clients = pd.base.clients.map { c => c.copy (nodePath = node +: c.nodePath) }))
|
pd.copy(base = pd.base.copy(clients = pd.base.clients.map { c => c.copy (nodePath = node +: c.nodePath) }))
|
||||||
override def mixI(pu: TLAsyncManagerPortParameters, node: InwardNode[TLAsyncClientPortParameters, TLAsyncManagerPortParameters, TLAsyncBundle]): TLAsyncManagerPortParameters =
|
override def mixI(pu: TLAsyncManagerPortParameters, node: InwardNode[TLAsyncClientPortParameters, TLAsyncManagerPortParameters, TLAsyncBundle]): TLAsyncManagerPortParameters =
|
||||||
@ -169,17 +172,15 @@ case class TLAsyncIdentityNode() extends IdentityNode(TLAsyncImp)
|
|||||||
case class TLAsyncOutputNode() extends OutputNode(TLAsyncImp)
|
case class TLAsyncOutputNode() extends OutputNode(TLAsyncImp)
|
||||||
case class TLAsyncInputNode() extends InputNode(TLAsyncImp)
|
case class TLAsyncInputNode() extends InputNode(TLAsyncImp)
|
||||||
|
|
||||||
case class TLAsyncSourceNode(sync: Int) extends MixedNode(TLImp, TLAsyncImp)(
|
case class TLAsyncSourceNode(sync: Int)
|
||||||
dFn = { case (1, Seq(p)) => Seq(TLAsyncClientPortParameters(p)) },
|
extends MixedAdapterNode(TLImp, TLAsyncImp)(
|
||||||
uFn = { case (1, Seq(p)) => Seq(p.base.copy(minLatency = sync+1)) }, // discard cycles in other clock domain
|
dFn = { p => TLAsyncClientPortParameters(p) },
|
||||||
numPO = 1 to 1,
|
uFn = { p => p.base.copy(minLatency = sync+1) }) // discard cycles in other clock domain
|
||||||
numPI = 1 to 1)
|
|
||||||
|
|
||||||
case class TLAsyncSinkNode(depth: Int, sync: Int) extends MixedNode(TLAsyncImp, TLImp)(
|
case class TLAsyncSinkNode(depth: Int, sync: Int)
|
||||||
dFn = { case (1, Seq(p)) => Seq(p.base.copy(minLatency = sync+1)) },
|
extends MixedAdapterNode(TLAsyncImp, TLImp)(
|
||||||
uFn = { case (1, Seq(p)) => Seq(TLAsyncManagerPortParameters(depth, p)) },
|
dFn = { p => p.base.copy(minLatency = sync+1) },
|
||||||
numPO = 1 to 1,
|
uFn = { p => TLAsyncManagerPortParameters(depth, p) })
|
||||||
numPI = 1 to 1)
|
|
||||||
|
|
||||||
object TLRationalImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TLEdgeParameters, TLEdgeParameters, TLRationalBundle]
|
object TLRationalImp extends NodeImp[TLClientPortParameters, TLManagerPortParameters, TLEdgeParameters, TLEdgeParameters, TLRationalBundle]
|
||||||
{
|
{
|
||||||
@ -191,10 +192,6 @@ object TLRationalImp extends NodeImp[TLClientPortParameters, TLManagerPortParame
|
|||||||
|
|
||||||
def colour = "#00ff00" // green
|
def colour = "#00ff00" // green
|
||||||
|
|
||||||
def connect(bo: => TLRationalBundle, bi: => TLRationalBundle, ei: => TLEdgeParameters)(implicit p: Parameters, sourceInfo: SourceInfo): (Option[LazyModule], () => Unit) = {
|
|
||||||
(None, () => { bi <> bo })
|
|
||||||
}
|
|
||||||
|
|
||||||
override def mixO(pd: TLClientPortParameters, node: OutwardNode[TLClientPortParameters, TLManagerPortParameters, TLRationalBundle]): TLClientPortParameters =
|
override def mixO(pd: TLClientPortParameters, node: OutwardNode[TLClientPortParameters, TLManagerPortParameters, TLRationalBundle]): TLClientPortParameters =
|
||||||
pd.copy(clients = pd.clients.map { c => c.copy (nodePath = node +: c.nodePath) })
|
pd.copy(clients = pd.clients.map { c => c.copy (nodePath = node +: c.nodePath) })
|
||||||
override def mixI(pu: TLManagerPortParameters, node: InwardNode[TLClientPortParameters, TLManagerPortParameters, TLRationalBundle]): TLManagerPortParameters =
|
override def mixI(pu: TLManagerPortParameters, node: InwardNode[TLClientPortParameters, TLManagerPortParameters, TLRationalBundle]): TLManagerPortParameters =
|
||||||
@ -205,14 +202,12 @@ case class TLRationalIdentityNode() extends IdentityNode(TLRationalImp)
|
|||||||
case class TLRationalOutputNode() extends OutputNode(TLRationalImp)
|
case class TLRationalOutputNode() extends OutputNode(TLRationalImp)
|
||||||
case class TLRationalInputNode() extends InputNode(TLRationalImp)
|
case class TLRationalInputNode() extends InputNode(TLRationalImp)
|
||||||
|
|
||||||
case class TLRationalSourceNode() extends MixedNode(TLImp, TLRationalImp)(
|
case class TLRationalSourceNode()
|
||||||
dFn = { case (_, s) => s },
|
extends MixedAdapterNode(TLImp, TLRationalImp)(
|
||||||
uFn = { case (_, s) => s.map(p => p.copy(minLatency = 1)) }, // discard cycles from other clock domain
|
dFn = { p => p },
|
||||||
numPO = 0 to 999,
|
uFn = { p => p.copy(minLatency = 1) }) // discard cycles from other clock domain
|
||||||
numPI = 0 to 999)
|
|
||||||
|
|
||||||
case class TLRationalSinkNode() extends MixedNode(TLRationalImp, TLImp)(
|
case class TLRationalSinkNode()
|
||||||
dFn = { case (_, s) => s.map(p => p.copy(minLatency = 1)) },
|
extends MixedAdapterNode(TLRationalImp, TLImp)(
|
||||||
uFn = { case (_, s) => s },
|
dFn = { p => p.copy(minLatency = 1) },
|
||||||
numPO = 0 to 999,
|
uFn = { p => p })
|
||||||
numPI = 0 to 999)
|
|
||||||
|
@ -5,6 +5,7 @@ package uncore.tilelink2
|
|||||||
import Chisel._
|
import Chisel._
|
||||||
import config._
|
import config._
|
||||||
import diplomacy._
|
import diplomacy._
|
||||||
|
import util.GenericParameterizedBundle
|
||||||
|
|
||||||
// We detect concurrent puts that put memory into an undefined state.
|
// We detect concurrent puts that put memory into an undefined state.
|
||||||
// put0, put0Ack, put1, put1Ack => ok: defined
|
// put0, put0Ack, put1, put1Ack => ok: defined
|
||||||
@ -31,268 +32,271 @@ class TLRAMModel(log: String = "")(implicit p: Parameters) extends LazyModule
|
|||||||
val out = node.bundleOut
|
val out = node.bundleOut
|
||||||
}
|
}
|
||||||
|
|
||||||
// !!! support multiple clients via clock division
|
((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
|
||||||
require (io.out.size == 1)
|
val edge = edgeIn
|
||||||
|
val endAddress = edge.manager.maxAddress + 1
|
||||||
|
val endSourceId = edge.client.endSourceId
|
||||||
|
val maxTransfer = edge.manager.maxTransfer
|
||||||
|
val beatBytes = edge.manager.beatBytes
|
||||||
|
val endAddressHi = (endAddress / beatBytes).intValue
|
||||||
|
val maxLgBeats = log2Up(maxTransfer/beatBytes)
|
||||||
|
val shift = log2Ceil(beatBytes)
|
||||||
|
val decTrees = log2Up(maxTransfer/beatBytes)
|
||||||
|
val addressBits = log2Up(endAddress)
|
||||||
|
val countBits = log2Up(endSourceId)
|
||||||
|
val sizeBits = edge.bundle.sizeBits
|
||||||
|
|
||||||
val in = io.in(0)
|
// Reset control logic
|
||||||
val out = io.out(0)
|
val wipeIndex = RegInit(UInt(0, width = log2Ceil(endAddressHi) + 1))
|
||||||
|
val wipe = !wipeIndex(log2Ceil(endAddressHi))
|
||||||
|
wipeIndex := wipeIndex + wipe.asUInt
|
||||||
|
|
||||||
val edge = node.edgesIn(0)
|
// Block traffic while wiping Mems
|
||||||
val endAddress = edge.manager.maxAddress + 1
|
in.a.ready := out.a.ready && !wipe
|
||||||
val endSourceId = edge.client.endSourceId
|
out.a.valid := in.a.valid && !wipe
|
||||||
val maxTransfer = edge.manager.maxTransfer
|
out.a.bits := in.a.bits
|
||||||
val beatBytes = edge.manager.beatBytes
|
out.d.ready := in.d.ready && !wipe
|
||||||
val endAddressHi = (endAddress / beatBytes).intValue
|
in.d.valid := out.d.valid && !wipe
|
||||||
val maxLgBeats = log2Up(maxTransfer/beatBytes)
|
in.d.bits := out.d.bits
|
||||||
val shift = log2Ceil(beatBytes)
|
|
||||||
val decTrees = log2Up(maxTransfer/beatBytes)
|
|
||||||
val addressBits = log2Up(endAddress)
|
|
||||||
val countBits = log2Up(endSourceId)
|
|
||||||
val sizeBits = edge.bundle.sizeBits
|
|
||||||
|
|
||||||
// Reset control logic
|
// BCE unsupported
|
||||||
val wipeIndex = RegInit(UInt(0, width = log2Ceil(endAddressHi) + 1))
|
in.b.valid := Bool(false)
|
||||||
val wipe = !wipeIndex(log2Ceil(endAddressHi))
|
out.c.valid := Bool(false)
|
||||||
wipeIndex := wipeIndex + wipe.asUInt
|
out.e.valid := Bool(false)
|
||||||
|
out.b.ready := Bool(true)
|
||||||
|
in.c.ready := Bool(true)
|
||||||
|
in.e.ready := Bool(true)
|
||||||
|
|
||||||
// Block traffic while wiping Mems
|
val params = TLRAMModel.MonitorParameters(addressBits, sizeBits)
|
||||||
in.a.ready := out.a.ready && !wipe
|
|
||||||
out.a.valid := in.a.valid && !wipe
|
|
||||||
out.a.bits := in.a.bits
|
|
||||||
out.d.ready := in.d.ready && !wipe
|
|
||||||
in.d.valid := out.d.valid && !wipe
|
|
||||||
in.d.bits := out.d.bits
|
|
||||||
|
|
||||||
// BCE unsupported
|
// Infer as simple dual port BRAM/M10k with write-first/new-data semantics (bypass needed)
|
||||||
in.b.valid := Bool(false)
|
val shadow = Seq.fill(beatBytes) { Mem(endAddressHi, new TLRAMModel.ByteMonitor(params)) }
|
||||||
out.c.valid := Bool(false)
|
val inc_bytes = Seq.fill(beatBytes) { Mem(endAddressHi, UInt(width = countBits)) }
|
||||||
out.e.valid := Bool(false)
|
val dec_bytes = Seq.fill(beatBytes) { Mem(endAddressHi, UInt(width = countBits)) }
|
||||||
out.b.ready := Bool(true)
|
val inc_trees = Seq.tabulate(decTrees) { i => Mem(endAddressHi >> (i+1), UInt(width = countBits)) }
|
||||||
in.c.ready := Bool(true)
|
val dec_trees = Seq.tabulate(decTrees) { i => Mem(endAddressHi >> (i+1), UInt(width = countBits)) }
|
||||||
in.e.ready := Bool(true)
|
|
||||||
|
|
||||||
class ByteMonitor extends Bundle {
|
val shadow_wen = Wire(init = Fill(beatBytes, wipe))
|
||||||
val valid = Bool()
|
val inc_bytes_wen = Wire(init = Fill(beatBytes, wipe))
|
||||||
val value = UInt(width = 8)
|
val dec_bytes_wen = Wire(init = Fill(beatBytes, wipe))
|
||||||
}
|
val inc_trees_wen = Wire(init = Fill(decTrees, wipe))
|
||||||
class FlightMonitor extends Bundle {
|
val dec_trees_wen = Wire(init = Fill(decTrees, wipe))
|
||||||
val base = UInt(width = addressBits)
|
|
||||||
val size = UInt(width = sizeBits)
|
|
||||||
val opcode = UInt(width = 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Infer as simple dual port BRAM/M10k with write-first/new-data semantics (bypass needed)
|
// This must be registers b/c we build a CAM from it
|
||||||
val shadow = Seq.fill(beatBytes) { Mem(endAddressHi, new ByteMonitor) }
|
val flight = Reg(Vec(endSourceId, new TLRAMModel.FlightMonitor(params)))
|
||||||
val inc_bytes = Seq.fill(beatBytes) { Mem(endAddressHi, UInt(width = countBits)) }
|
val valid = Reg(Vec(endSourceId, Bool()))
|
||||||
val dec_bytes = Seq.fill(beatBytes) { Mem(endAddressHi, UInt(width = countBits)) }
|
|
||||||
val inc_trees = Seq.tabulate(decTrees) { i => Mem(endAddressHi >> (i+1), UInt(width = countBits)) }
|
|
||||||
val dec_trees = Seq.tabulate(decTrees) { i => Mem(endAddressHi >> (i+1), UInt(width = countBits)) }
|
|
||||||
|
|
||||||
val shadow_wen = Wire(init = Fill(beatBytes, wipe))
|
// We want to cross flight data from A to D in the same cycle (for combinational TL2 devices)
|
||||||
val inc_bytes_wen = Wire(init = Fill(beatBytes, wipe))
|
val a_flight = Wire(new TLRAMModel.FlightMonitor(params))
|
||||||
val dec_bytes_wen = Wire(init = Fill(beatBytes, wipe))
|
a_flight.base := edge.address(in.a.bits)
|
||||||
val inc_trees_wen = Wire(init = Fill(decTrees, wipe))
|
a_flight.size := edge.size(in.a.bits)
|
||||||
val dec_trees_wen = Wire(init = Fill(decTrees, wipe))
|
a_flight.opcode := in.a.bits.opcode
|
||||||
|
|
||||||
// This must be registers b/c we build a CAM from it
|
when (in.a.fire()) { flight(in.a.bits.source) := a_flight }
|
||||||
val flight = Reg(Vec(endSourceId, new FlightMonitor))
|
val bypass = if (edge.manager.minLatency > 0) Bool(false) else in.a.valid && in.a.bits.source === out.d.bits.source
|
||||||
val valid = Reg(Vec(endSourceId, Bool()))
|
val d_flight = RegNext(Mux(bypass, a_flight, flight(out.d.bits.source)))
|
||||||
|
|
||||||
// We want to cross flight data from A to D in the same cycle (for combinational TL2 devices)
|
// Process A access requests
|
||||||
val a_flight = Wire(new FlightMonitor)
|
val a = Reg(next = in.a.bits)
|
||||||
a_flight.base := edge.address(in.a.bits)
|
val a_fire = Reg(next = in.a.fire(), init = Bool(false))
|
||||||
a_flight.size := edge.size(in.a.bits)
|
val (a_first, a_last, _, a_address_inc) = edge.addr_inc(a, a_fire)
|
||||||
a_flight.opcode := in.a.bits.opcode
|
val a_size = edge.size(a)
|
||||||
|
val a_sizeOH = UIntToOH(a_size)
|
||||||
|
val a_address = a.address | a_address_inc
|
||||||
|
val a_addr_hi = edge.addr_hi(a_address)
|
||||||
|
val a_base = edge.address(a)
|
||||||
|
val a_mask = edge.mask(a_base, a_size)
|
||||||
|
val a_fifo = edge.manager.hasFifoIdFast(a_base)
|
||||||
|
|
||||||
when (in.a.fire()) { flight(in.a.bits.source) := a_flight }
|
// Grab the concurrency state we need
|
||||||
val bypass = if (edge.manager.minLatency > 0) Bool(false) else in.a.valid && in.a.bits.source === out.d.bits.source
|
val a_inc_bytes = inc_bytes.map(_.read(a_addr_hi))
|
||||||
val d_flight = RegNext(Mux(bypass, a_flight, flight(out.d.bits.source)))
|
val a_dec_bytes = dec_bytes.map(_.read(a_addr_hi))
|
||||||
|
val a_inc_trees = inc_trees.zipWithIndex.map{ case (m, i) => m.read(a_addr_hi >> (i+1)) }
|
||||||
|
val a_dec_trees = dec_trees.zipWithIndex.map{ case (m, i) => m.read(a_addr_hi >> (i+1)) }
|
||||||
|
val a_inc_tree = a_inc_trees.fold(UInt(0))(_ + _)
|
||||||
|
val a_dec_tree = a_dec_trees.fold(UInt(0))(_ + _)
|
||||||
|
val a_inc = a_inc_bytes.map(_ + a_inc_tree)
|
||||||
|
val a_dec = a_dec_bytes.map(_ + a_dec_tree)
|
||||||
|
|
||||||
// Process A access requests
|
when (a_fire) {
|
||||||
val a = Reg(next = in.a.bits)
|
// Record the request so we can handle it's response
|
||||||
val a_fire = Reg(next = in.a.fire(), init = Bool(false))
|
assert (a.opcode =/= TLMessages.Acquire)
|
||||||
val (a_first, a_last, _, a_address_inc) = edge.addr_inc(a, a_fire)
|
|
||||||
val a_size = edge.size(a)
|
|
||||||
val a_sizeOH = UIntToOH(a_size)
|
|
||||||
val a_address = a.address | a_address_inc
|
|
||||||
val a_addr_hi = edge.addr_hi(a_address)
|
|
||||||
val a_base = edge.address(a)
|
|
||||||
val a_mask = edge.mask(a_base, a_size)
|
|
||||||
val a_fifo = edge.manager.hasFifoIdFast(a_base)
|
|
||||||
|
|
||||||
// Grab the concurrency state we need
|
// Mark the operation as valid
|
||||||
val a_inc_bytes = inc_bytes.map(_.read(a_addr_hi))
|
valid(a.source) := Bool(true)
|
||||||
val a_dec_bytes = dec_bytes.map(_.read(a_addr_hi))
|
|
||||||
val a_inc_trees = inc_trees.zipWithIndex.map{ case (m, i) => m.read(a_addr_hi >> (i+1)) }
|
|
||||||
val a_dec_trees = dec_trees.zipWithIndex.map{ case (m, i) => m.read(a_addr_hi >> (i+1)) }
|
|
||||||
val a_inc_tree = a_inc_trees.fold(UInt(0))(_ + _)
|
|
||||||
val a_dec_tree = a_dec_trees.fold(UInt(0))(_ + _)
|
|
||||||
val a_inc = a_inc_bytes.map(_ + a_inc_tree)
|
|
||||||
val a_dec = a_dec_bytes.map(_ + a_dec_tree)
|
|
||||||
|
|
||||||
when (a_fire) {
|
// Increase the per-byte flight counter for the whole transaction
|
||||||
// Record the request so we can handle it's response
|
when (a_first && a.opcode =/= TLMessages.Hint && a.opcode =/= TLMessages.Get) {
|
||||||
assert (a.opcode =/= TLMessages.Acquire)
|
when (a_size <= UInt(shift)) {
|
||||||
|
inc_bytes_wen := a_mask
|
||||||
// Mark the operation as valid
|
}
|
||||||
valid(a.source) := Bool(true)
|
inc_trees_wen := a_sizeOH >> (shift+1)
|
||||||
|
|
||||||
// Increase the per-byte flight counter for the whole transaction
|
|
||||||
when (a_first && a.opcode =/= TLMessages.Hint && a.opcode =/= TLMessages.Get) {
|
|
||||||
when (a_size <= UInt(shift)) {
|
|
||||||
inc_bytes_wen := a_mask
|
|
||||||
}
|
}
|
||||||
inc_trees_wen := a_sizeOH >> (shift+1)
|
|
||||||
}
|
|
||||||
|
|
||||||
when (a.opcode === TLMessages.PutFullData || a.opcode === TLMessages.PutPartialData ||
|
when (a.opcode === TLMessages.PutFullData || a.opcode === TLMessages.PutPartialData ||
|
||||||
a.opcode === TLMessages.ArithmeticData || a.opcode === TLMessages.LogicalData) {
|
a.opcode === TLMessages.ArithmeticData || a.opcode === TLMessages.LogicalData) {
|
||||||
shadow_wen := a.mask
|
shadow_wen := a.mask
|
||||||
for (i <- 0 until beatBytes) {
|
for (i <- 0 until beatBytes) {
|
||||||
val busy = a_inc(i) - a_dec(i) - (!a_first).asUInt
|
val busy = a_inc(i) - a_dec(i) - (!a_first).asUInt
|
||||||
val byte = a.data(8*(i+1)-1, 8*i)
|
val byte = a.data(8*(i+1)-1, 8*i)
|
||||||
when (a.mask(i)) {
|
when (a.mask(i)) {
|
||||||
printf(log + " ")
|
printf(log + " ")
|
||||||
when (a.opcode === TLMessages.PutFullData) { printf("PF") }
|
when (a.opcode === TLMessages.PutFullData) { printf("PF") }
|
||||||
when (a.opcode === TLMessages.PutPartialData) { printf("PP") }
|
when (a.opcode === TLMessages.PutPartialData) { printf("PP") }
|
||||||
when (a.opcode === TLMessages.ArithmeticData) { printf("A ") }
|
when (a.opcode === TLMessages.ArithmeticData) { printf("A ") }
|
||||||
when (a.opcode === TLMessages.LogicalData) { printf("L ") }
|
when (a.opcode === TLMessages.LogicalData) { printf("L ") }
|
||||||
printf(" 0x%x := 0x%x #%d %x\n", a_addr_hi << shift | UInt(i), byte, busy, a.param)
|
printf(" 0x%x := 0x%x #%d %x\n", a_addr_hi << shift | UInt(i), byte, busy, a.param)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
when (a.opcode === TLMessages.Get) {
|
when (a.opcode === TLMessages.Get) {
|
||||||
printf(log + " G 0x%x - 0%x\n", a_base, a_base | UIntToOH1(a_size, addressBits))
|
printf(log + " G 0x%x - 0%x\n", a_base, a_base | UIntToOH1(a_size, addressBits))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val a_waddr = Mux(wipe, wipeIndex, a_addr_hi)
|
|
||||||
for (i <- 0 until beatBytes) {
|
|
||||||
val data = Wire(new ByteMonitor)
|
|
||||||
val busy = a_inc(i) =/= a_dec(i) + (!a_first).asUInt
|
|
||||||
val amo = a.opcode === TLMessages.ArithmeticData || a.opcode === TLMessages.LogicalData
|
|
||||||
data.valid := Mux(wipe, Bool(false), (!busy || a_fifo) && !amo)
|
|
||||||
// !!! calculate the AMO?
|
|
||||||
data.value := a.data(8*(i+1)-1, 8*i)
|
|
||||||
when (shadow_wen(i)) {
|
|
||||||
shadow(i).write(a_waddr, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i <- 0 until beatBytes) {
|
|
||||||
val data = Mux(wipe, UInt(0), a_inc_bytes(i) + UInt(1))
|
|
||||||
when (inc_bytes_wen(i)) {
|
|
||||||
inc_bytes(i).write(a_waddr, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i <- 0 until inc_trees.size) {
|
|
||||||
val data = Mux(wipe, UInt(0), a_inc_trees(i) + UInt(1))
|
|
||||||
when (inc_trees_wen(i)) {
|
|
||||||
inc_trees(i).write(a_waddr >> (i+1), data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process D access responses
|
|
||||||
val d = RegNext(out.d.bits)
|
|
||||||
val d_fire = Reg(next = out.d.fire(), init = Bool(false))
|
|
||||||
val (d_first, d_last, _, d_address_inc) = edge.addr_inc(d, d_fire)
|
|
||||||
val d_size = edge.size(d)
|
|
||||||
val d_sizeOH = UIntToOH(d_size)
|
|
||||||
val d_base = d_flight.base
|
|
||||||
val d_address = d_base | d_address_inc
|
|
||||||
val d_addr_hi = edge.addr_hi(d_address)
|
|
||||||
val d_mask = edge.mask(d_base, d_size)
|
|
||||||
val d_fifo = edge.manager.hasFifoIdFast(d_flight.base)
|
|
||||||
|
|
||||||
// Grab the concurrency state we need
|
|
||||||
val d_inc_bytes = inc_bytes.map(_.read(d_addr_hi))
|
|
||||||
val d_dec_bytes = dec_bytes.map(_.read(d_addr_hi))
|
|
||||||
val d_inc_trees = inc_trees.zipWithIndex.map{ case (m, i) => m.read(d_addr_hi >> (i+1)) }
|
|
||||||
val d_dec_trees = dec_trees.zipWithIndex.map{ case (m, i) => m.read(d_addr_hi >> (i+1)) }
|
|
||||||
val d_inc_tree = d_inc_trees.fold(UInt(0))(_ + _)
|
|
||||||
val d_dec_tree = d_dec_trees.fold(UInt(0))(_ + _)
|
|
||||||
val d_inc = d_inc_bytes.map(_ + d_inc_tree)
|
|
||||||
val d_dec = d_dec_bytes.map(_ + d_dec_tree)
|
|
||||||
val d_shadow = shadow.map(_.read(d_addr_hi))
|
|
||||||
val d_valid = valid(d.source)
|
|
||||||
|
|
||||||
when (d_fire) {
|
|
||||||
// Check the response is correct
|
|
||||||
assert (d_size === d_flight.size)
|
|
||||||
// addr_lo is allowed to differ
|
|
||||||
|
|
||||||
when (d_flight.opcode === TLMessages.Hint) {
|
|
||||||
assert (d.opcode === TLMessages.HintAck)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrease the per-byte flight counter for the whole transaction
|
|
||||||
when (d_last && d_flight.opcode =/= TLMessages.Hint && d_flight.opcode =/= TLMessages.Get) {
|
|
||||||
when (d_size <= UInt(shift)) {
|
|
||||||
dec_bytes_wen := d_mask
|
|
||||||
}
|
|
||||||
dec_trees_wen := d_sizeOH >> (shift+1)
|
|
||||||
// NOTE: D channel carries uninterrupted multibeast op, so updating on last is fine
|
|
||||||
for (i <- 0 until endSourceId) {
|
|
||||||
// Does this modification overlap a Get? => wipe it's valid
|
|
||||||
val f_base = flight(i).base
|
|
||||||
val f_size = flight(i).size
|
|
||||||
val f_bits = UIntToOH1(f_size, addressBits)
|
|
||||||
val d_bits = UIntToOH1(d_size, addressBits)
|
|
||||||
val overlap = ~(~(f_base ^ d_base) | (f_bits | d_bits)) === UInt(0)
|
|
||||||
when (overlap) { valid(i) := Bool(false) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when (d_flight.opcode === TLMessages.PutFullData || d_flight.opcode === TLMessages.PutPartialData) {
|
val a_waddr = Mux(wipe, wipeIndex, a_addr_hi)
|
||||||
assert (d.opcode === TLMessages.AccessAck)
|
for (i <- 0 until beatBytes) {
|
||||||
printf(log + " ")
|
val data = Wire(new TLRAMModel.ByteMonitor(params))
|
||||||
when (d_flight.opcode === TLMessages.PutFullData) { printf("pf") }
|
val busy = a_inc(i) =/= a_dec(i) + (!a_first).asUInt
|
||||||
when (d_flight.opcode === TLMessages.PutPartialData) { printf("pp") }
|
val amo = a.opcode === TLMessages.ArithmeticData || a.opcode === TLMessages.LogicalData
|
||||||
printf(" 0x%x - 0x%x\n", d_base, d_base | UIntToOH1(d_size, addressBits))
|
data.valid := Mux(wipe, Bool(false), (!busy || a_fifo) && !amo)
|
||||||
|
// !!! calculate the AMO?
|
||||||
|
data.value := a.data(8*(i+1)-1, 8*i)
|
||||||
|
when (shadow_wen(i)) {
|
||||||
|
shadow(i).write(a_waddr, data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when (d_flight.opcode === TLMessages.Get || d_flight.opcode === TLMessages.ArithmeticData || d_flight.opcode === TLMessages.LogicalData) {
|
for (i <- 0 until beatBytes) {
|
||||||
assert (d.opcode === TLMessages.AccessAckData)
|
val data = Mux(wipe, UInt(0), a_inc_bytes(i) + UInt(1))
|
||||||
for (i <- 0 until beatBytes) {
|
when (inc_bytes_wen(i)) {
|
||||||
val got = d.data(8*(i+1)-1, 8*i)
|
inc_bytes(i).write(a_waddr, data)
|
||||||
val shadow = Wire(init = d_shadow(i))
|
}
|
||||||
when (d_mask(i)) {
|
}
|
||||||
val d_addr = d_addr_hi << shift | UInt(i)
|
|
||||||
printf(log + " ")
|
for (i <- 0 until inc_trees.size) {
|
||||||
when (d_flight.opcode === TLMessages.Get) { printf("g ") }
|
val data = Mux(wipe, UInt(0), a_inc_trees(i) + UInt(1))
|
||||||
when (d_flight.opcode === TLMessages.ArithmeticData) { printf("a ") }
|
when (inc_trees_wen(i)) {
|
||||||
when (d_flight.opcode === TLMessages.LogicalData) { printf("l ") }
|
inc_trees(i).write(a_waddr >> (i+1), data)
|
||||||
printf(" 0x%x := 0x%x", d_addr, got)
|
}
|
||||||
when (!shadow.valid) {
|
}
|
||||||
printf(", undefined (uninitialized or prior overlapping puts)\n")
|
|
||||||
} .elsewhen (d_inc(i) =/= d_dec(i)) {
|
// Process D access responses
|
||||||
printf(", undefined (concurrent incomplete puts #%d)\n", d_inc(i) - d_dec(i))
|
val d = RegNext(out.d.bits)
|
||||||
} .elsewhen (!d_fifo && !d_valid) {
|
val d_fire = Reg(next = out.d.fire(), init = Bool(false))
|
||||||
printf(", undefined (concurrent completed put)\n")
|
val (d_first, d_last, _, d_address_inc) = edge.addr_inc(d, d_fire)
|
||||||
} .otherwise {
|
val d_size = edge.size(d)
|
||||||
printf("\n")
|
val d_sizeOH = UIntToOH(d_size)
|
||||||
assert (shadow.value === got)
|
val d_base = d_flight.base
|
||||||
|
val d_address = d_base | d_address_inc
|
||||||
|
val d_addr_hi = edge.addr_hi(d_address)
|
||||||
|
val d_mask = edge.mask(d_base, d_size)
|
||||||
|
val d_fifo = edge.manager.hasFifoIdFast(d_flight.base)
|
||||||
|
|
||||||
|
// Grab the concurrency state we need
|
||||||
|
val d_inc_bytes = inc_bytes.map(_.read(d_addr_hi))
|
||||||
|
val d_dec_bytes = dec_bytes.map(_.read(d_addr_hi))
|
||||||
|
val d_inc_trees = inc_trees.zipWithIndex.map{ case (m, i) => m.read(d_addr_hi >> (i+1)) }
|
||||||
|
val d_dec_trees = dec_trees.zipWithIndex.map{ case (m, i) => m.read(d_addr_hi >> (i+1)) }
|
||||||
|
val d_inc_tree = d_inc_trees.fold(UInt(0))(_ + _)
|
||||||
|
val d_dec_tree = d_dec_trees.fold(UInt(0))(_ + _)
|
||||||
|
val d_inc = d_inc_bytes.map(_ + d_inc_tree)
|
||||||
|
val d_dec = d_dec_bytes.map(_ + d_dec_tree)
|
||||||
|
val d_shadow = shadow.map(_.read(d_addr_hi))
|
||||||
|
val d_valid = valid(d.source)
|
||||||
|
|
||||||
|
when (d_fire) {
|
||||||
|
// Check the response is correct
|
||||||
|
assert (d_size === d_flight.size)
|
||||||
|
// addr_lo is allowed to differ
|
||||||
|
|
||||||
|
when (d_flight.opcode === TLMessages.Hint) {
|
||||||
|
assert (d.opcode === TLMessages.HintAck)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrease the per-byte flight counter for the whole transaction
|
||||||
|
when (d_last && d_flight.opcode =/= TLMessages.Hint && d_flight.opcode =/= TLMessages.Get) {
|
||||||
|
when (d_size <= UInt(shift)) {
|
||||||
|
dec_bytes_wen := d_mask
|
||||||
|
}
|
||||||
|
dec_trees_wen := d_sizeOH >> (shift+1)
|
||||||
|
// NOTE: D channel carries uninterrupted multibeast op, so updating on last is fine
|
||||||
|
for (i <- 0 until endSourceId) {
|
||||||
|
// Does this modification overlap a Get? => wipe it's valid
|
||||||
|
val f_base = flight(i).base
|
||||||
|
val f_size = flight(i).size
|
||||||
|
val f_bits = UIntToOH1(f_size, addressBits)
|
||||||
|
val d_bits = UIntToOH1(d_size, addressBits)
|
||||||
|
val overlap = ~(~(f_base ^ d_base) | (f_bits | d_bits)) === UInt(0)
|
||||||
|
when (overlap) { valid(i) := Bool(false) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when (d_flight.opcode === TLMessages.PutFullData || d_flight.opcode === TLMessages.PutPartialData) {
|
||||||
|
assert (d.opcode === TLMessages.AccessAck)
|
||||||
|
printf(log + " ")
|
||||||
|
when (d_flight.opcode === TLMessages.PutFullData) { printf("pf") }
|
||||||
|
when (d_flight.opcode === TLMessages.PutPartialData) { printf("pp") }
|
||||||
|
printf(" 0x%x - 0x%x\n", d_base, d_base | UIntToOH1(d_size, addressBits))
|
||||||
|
}
|
||||||
|
|
||||||
|
when (d_flight.opcode === TLMessages.Get || d_flight.opcode === TLMessages.ArithmeticData || d_flight.opcode === TLMessages.LogicalData) {
|
||||||
|
assert (d.opcode === TLMessages.AccessAckData)
|
||||||
|
for (i <- 0 until beatBytes) {
|
||||||
|
val got = d.data(8*(i+1)-1, 8*i)
|
||||||
|
val shadow = Wire(init = d_shadow(i))
|
||||||
|
when (d_mask(i)) {
|
||||||
|
val d_addr = d_addr_hi << shift | UInt(i)
|
||||||
|
printf(log + " ")
|
||||||
|
when (d_flight.opcode === TLMessages.Get) { printf("g ") }
|
||||||
|
when (d_flight.opcode === TLMessages.ArithmeticData) { printf("a ") }
|
||||||
|
when (d_flight.opcode === TLMessages.LogicalData) { printf("l ") }
|
||||||
|
printf(" 0x%x := 0x%x", d_addr, got)
|
||||||
|
when (!shadow.valid) {
|
||||||
|
printf(", undefined (uninitialized or prior overlapping puts)\n")
|
||||||
|
} .elsewhen (d_inc(i) =/= d_dec(i)) {
|
||||||
|
printf(", undefined (concurrent incomplete puts #%d)\n", d_inc(i) - d_dec(i))
|
||||||
|
} .elsewhen (!d_fifo && !d_valid) {
|
||||||
|
printf(", undefined (concurrent completed put)\n")
|
||||||
|
} .otherwise {
|
||||||
|
printf("\n")
|
||||||
|
assert (shadow.value === got)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
val d_waddr = Mux(wipe, wipeIndex, d_addr_hi)
|
val d_waddr = Mux(wipe, wipeIndex, d_addr_hi)
|
||||||
for (i <- 0 until beatBytes) {
|
for (i <- 0 until beatBytes) {
|
||||||
val data = Mux(wipe, UInt(0), d_dec_bytes(i) + UInt(1))
|
val data = Mux(wipe, UInt(0), d_dec_bytes(i) + UInt(1))
|
||||||
when (dec_bytes_wen(i)) {
|
when (dec_bytes_wen(i)) {
|
||||||
dec_bytes(i).write(d_waddr, data)
|
dec_bytes(i).write(d_waddr, data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for (i <- 0 until dec_trees.size) {
|
for (i <- 0 until dec_trees.size) {
|
||||||
val data = Mux(wipe, UInt(0), d_dec_trees(i) + UInt(1))
|
val data = Mux(wipe, UInt(0), d_dec_trees(i) + UInt(1))
|
||||||
when (dec_trees_wen(i)) {
|
when (dec_trees_wen(i)) {
|
||||||
dec_trees(i).write(d_waddr >> (i+1), data)
|
dec_trees(i).write(d_waddr >> (i+1), data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object TLRAMModel
|
||||||
|
{
|
||||||
|
case class MonitorParameters(addressBits: Int, sizeBits: Int)
|
||||||
|
|
||||||
|
class ByteMonitor(params: MonitorParameters) extends GenericParameterizedBundle(params) {
|
||||||
|
val valid = Bool()
|
||||||
|
val value = UInt(width = 8)
|
||||||
|
}
|
||||||
|
class FlightMonitor(params: MonitorParameters) extends GenericParameterizedBundle(params) {
|
||||||
|
val base = UInt(width = params.addressBits)
|
||||||
|
val size = UInt(width = params.sizeBits)
|
||||||
|
val opcode = UInt(width = 3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,7 +9,7 @@ import regmapper._
|
|||||||
import scala.math.{min,max}
|
import scala.math.{min,max}
|
||||||
|
|
||||||
class TLRegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false)
|
class TLRegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int = 4, undefZero: Boolean = true, executable: Boolean = false)
|
||||||
extends TLManagerNode(TLManagerPortParameters(
|
extends TLManagerNode(Seq(TLManagerPortParameters(
|
||||||
Seq(TLManagerParameters(
|
Seq(TLManagerParameters(
|
||||||
address = Seq(address),
|
address = Seq(address),
|
||||||
executable = executable,
|
executable = executable,
|
||||||
@ -18,7 +18,7 @@ class TLRegisterNode(address: AddressSet, concurrency: Int = 0, beatBytes: Int =
|
|||||||
supportsPutFull = TransferSizes(1, beatBytes),
|
supportsPutFull = TransferSizes(1, beatBytes),
|
||||||
fifoId = Some(0))), // requests are handled in order
|
fifoId = Some(0))), // requests are handled in order
|
||||||
beatBytes = beatBytes,
|
beatBytes = beatBytes,
|
||||||
minLatency = min(concurrency, 1))) // the Queue adds at most one cycle
|
minLatency = min(concurrency, 1)))) // the Queue adds at most one cycle
|
||||||
{
|
{
|
||||||
require (address.contiguous)
|
require (address.contiguous)
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import diplomacy._
|
|||||||
|
|
||||||
class TLRAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule
|
class TLRAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule
|
||||||
{
|
{
|
||||||
val node = TLManagerNode(TLManagerPortParameters(
|
val node = TLManagerNode(Seq(TLManagerPortParameters(
|
||||||
Seq(TLManagerParameters(
|
Seq(TLManagerParameters(
|
||||||
address = List(address),
|
address = List(address),
|
||||||
regionType = RegionType.UNCACHED,
|
regionType = RegionType.UNCACHED,
|
||||||
@ -18,7 +18,7 @@ class TLRAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)
|
|||||||
supportsPutFull = TransferSizes(1, beatBytes),
|
supportsPutFull = TransferSizes(1, beatBytes),
|
||||||
fifoId = Some(0))), // requests are handled in order
|
fifoId = Some(0))), // requests are handled in order
|
||||||
beatBytes = beatBytes,
|
beatBytes = beatBytes,
|
||||||
minLatency = 1)) // no bypass needed for this device
|
minLatency = 1))) // no bypass needed for this device
|
||||||
|
|
||||||
// We require the address range to include an entire beat (for the write mask)
|
// We require the address range to include an entire beat (for the write mask)
|
||||||
require ((address.mask & (beatBytes-1)) == beatBytes-1)
|
require ((address.mask & (beatBytes-1)) == beatBytes-1)
|
||||||
|
@ -15,8 +15,8 @@ class TLSourceShrinker(maxInFlight: Int)(implicit p: Parameters) extends LazyMod
|
|||||||
private val client = TLClientParameters(sourceId = IdRange(0, maxInFlight))
|
private val client = TLClientParameters(sourceId = IdRange(0, maxInFlight))
|
||||||
val node = TLAdapterNode(
|
val node = TLAdapterNode(
|
||||||
// We erase all client information since we crush the source Ids
|
// We erase all client information since we crush the source Ids
|
||||||
clientFn = { case _ => TLClientPortParameters(clients = Seq(client)) },
|
clientFn = { _ => TLClientPortParameters(clients = Seq(client)) },
|
||||||
managerFn = { case Seq(mp) => mp })
|
managerFn = { mp => mp })
|
||||||
|
|
||||||
lazy val module = new LazyModuleImp(this) {
|
lazy val module = new LazyModuleImp(this) {
|
||||||
val io = new Bundle {
|
val io = new Bundle {
|
||||||
@ -24,54 +24,51 @@ class TLSourceShrinker(maxInFlight: Int)(implicit p: Parameters) extends LazyMod
|
|||||||
val out = node.bundleOut
|
val out = node.bundleOut
|
||||||
}
|
}
|
||||||
|
|
||||||
val edgeIn = node.edgesIn(0)
|
((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
|
||||||
val edgeOut = node.edgesOut(0)
|
// Acquires cannot pass this adapter; it makes Probes impossible
|
||||||
val in = io.in(0)
|
require (!edgeIn.client.anySupportProbe ||
|
||||||
val out = io.out(0)
|
!edgeOut.manager.anySupportAcquireB)
|
||||||
|
|
||||||
// Acquires cannot pass this adapter; it makes Probes impossible
|
out.b.ready := Bool(true)
|
||||||
require (!edgeIn.client.anySupportProbe ||
|
out.c.valid := Bool(false)
|
||||||
!edgeOut.manager.anySupportAcquireB)
|
out.e.valid := Bool(false)
|
||||||
|
in.b.valid := Bool(false)
|
||||||
|
in.c.ready := Bool(true)
|
||||||
|
in.e.ready := Bool(true)
|
||||||
|
|
||||||
out.b.ready := Bool(true)
|
if (maxInFlight >= edgeIn.client.endSourceId) {
|
||||||
out.c.valid := Bool(false)
|
out.a <> in.a
|
||||||
out.e.valid := Bool(false)
|
in.d <> out.d
|
||||||
in.b.valid := Bool(false)
|
} else {
|
||||||
in.c.ready := Bool(true)
|
// State tracking
|
||||||
in.e.ready := Bool(true)
|
val sourceIdMap = Mem(maxInFlight, in.a.bits.source)
|
||||||
|
val allocated = RegInit(UInt(0, width = maxInFlight))
|
||||||
|
val nextFreeOH = ~(leftOR(~allocated) << 1) & ~allocated
|
||||||
|
val nextFree = OHToUInt(nextFreeOH)
|
||||||
|
val full = allocated.andR()
|
||||||
|
|
||||||
if (maxInFlight >= edgeIn.client.endSourceId) {
|
val a_first = edgeIn.first(in.a)
|
||||||
out.a <> in.a
|
val d_last = edgeIn.last(in.d)
|
||||||
in.d <> out.d
|
|
||||||
} else {
|
|
||||||
// State tracking
|
|
||||||
val sourceIdMap = Mem(maxInFlight, in.a.bits.source)
|
|
||||||
val allocated = RegInit(UInt(0, width = maxInFlight))
|
|
||||||
val nextFreeOH = ~(leftOR(~allocated) << 1) & ~allocated
|
|
||||||
val nextFree = OHToUInt(nextFreeOH)
|
|
||||||
val full = allocated.andR()
|
|
||||||
|
|
||||||
val a_first = edgeIn.first(in.a)
|
val block = a_first && full
|
||||||
val d_last = edgeIn.last(in.d)
|
in.a.ready := out.a.ready && !block
|
||||||
|
out.a.valid := in.a.valid && !block
|
||||||
|
out.a.bits := in.a.bits
|
||||||
|
out.a.bits.source := holdUnless(nextFree, a_first)
|
||||||
|
|
||||||
val block = a_first && full
|
in.d <> out.d
|
||||||
in.a.ready := out.a.ready && !block
|
in.d.bits.source := sourceIdMap(out.d.bits.source)
|
||||||
out.a.valid := in.a.valid && !block
|
|
||||||
out.a.bits := in.a.bits
|
|
||||||
out.a.bits.source := holdUnless(nextFree, a_first)
|
|
||||||
|
|
||||||
in.d <> out.d
|
when (a_first && in.a.fire()) {
|
||||||
in.d.bits.source := sourceIdMap(out.d.bits.source)
|
sourceIdMap(nextFree) := in.a.bits.source
|
||||||
|
}
|
||||||
|
|
||||||
when (a_first && in.a.fire()) {
|
val alloc = a_first && in.a.fire()
|
||||||
sourceIdMap(nextFree) := in.a.bits.source
|
val free = d_last && in.d.fire()
|
||||||
|
val alloc_id = Mux(alloc, nextFreeOH, UInt(0))
|
||||||
|
val free_id = Mux(free, UIntToOH(out.d.bits.source), UInt(0))
|
||||||
|
allocated := (allocated | alloc_id) & ~free_id
|
||||||
}
|
}
|
||||||
|
|
||||||
val alloc = a_first && in.a.fire()
|
|
||||||
val free = d_last && in.d.fire()
|
|
||||||
val alloc_id = Mux(alloc, nextFreeOH, UInt(0))
|
|
||||||
val free_id = Mux(free, UIntToOH(out.d.bits.source), UInt(0))
|
|
||||||
allocated := (allocated | alloc_id) & ~free_id
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,12 +10,12 @@ import uncore.ahb._
|
|||||||
import scala.math.{min, max}
|
import scala.math.{min, max}
|
||||||
import AHBParameters._
|
import AHBParameters._
|
||||||
|
|
||||||
case class TLToAHBNode() extends MixedNode(TLImp, AHBImp)(
|
case class TLToAHBNode() extends MixedAdapterNode(TLImp, AHBImp)(
|
||||||
dFn = { case (1, Seq(TLClientPortParameters(clients, unsafeAtomics, minLatency))) =>
|
dFn = { case TLClientPortParameters(clients, unsafeAtomics, minLatency) =>
|
||||||
val masters = clients.map { case c => AHBMasterParameters(nodePath = c.nodePath) }
|
val masters = clients.map { case c => AHBMasterParameters(nodePath = c.nodePath) }
|
||||||
Seq(AHBMasterPortParameters(masters))
|
AHBMasterPortParameters(masters)
|
||||||
},
|
},
|
||||||
uFn = { case (1, Seq(AHBSlavePortParameters(slaves, beatBytes))) =>
|
uFn = { case AHBSlavePortParameters(slaves, beatBytes) =>
|
||||||
val managers = slaves.map { case s =>
|
val managers = slaves.map { case s =>
|
||||||
TLManagerParameters(
|
TLManagerParameters(
|
||||||
address = s.address,
|
address = s.address,
|
||||||
@ -26,10 +26,8 @@ case class TLToAHBNode() extends MixedNode(TLImp, AHBImp)(
|
|||||||
supportsPutFull = s.supportsWrite, // but not PutPartial
|
supportsPutFull = s.supportsWrite, // but not PutPartial
|
||||||
fifoId = Some(0)) // a common FIFO domain
|
fifoId = Some(0)) // a common FIFO domain
|
||||||
}
|
}
|
||||||
Seq(TLManagerPortParameters(managers, beatBytes, 1, 1))
|
TLManagerPortParameters(managers, beatBytes, 1, 1)
|
||||||
},
|
})
|
||||||
numPO = 1 to 1,
|
|
||||||
numPI = 1 to 1)
|
|
||||||
|
|
||||||
class TLToAHB(combinational: Boolean = true)(implicit p: Parameters) extends LazyModule
|
class TLToAHB(combinational: Boolean = true)(implicit p: Parameters) extends LazyModule
|
||||||
{
|
{
|
||||||
@ -41,91 +39,89 @@ class TLToAHB(combinational: Boolean = true)(implicit p: Parameters) extends Laz
|
|||||||
val out = node.bundleOut
|
val out = node.bundleOut
|
||||||
}
|
}
|
||||||
|
|
||||||
val in = io.in(0)
|
((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
|
||||||
val out = io.out(0)
|
val beatBytes = edgeOut.slave.beatBytes
|
||||||
val edgeIn = node.edgesIn(0)
|
val maxTransfer = edgeOut.slave.maxTransfer
|
||||||
val edgeOut = node.edgesOut(0)
|
val lgMax = log2Ceil(maxTransfer)
|
||||||
val beatBytes = edgeOut.slave.beatBytes
|
val lgBytes = log2Ceil(beatBytes)
|
||||||
val maxTransfer = edgeOut.slave.maxTransfer
|
|
||||||
val lgMax = log2Ceil(maxTransfer)
|
|
||||||
val lgBytes = log2Ceil(beatBytes)
|
|
||||||
|
|
||||||
// AHB has no cache coherence
|
// AHB has no cache coherence
|
||||||
in.b.valid := Bool(false)
|
in.b.valid := Bool(false)
|
||||||
in.c.ready := Bool(true)
|
in.c.ready := Bool(true)
|
||||||
in.e.ready := Bool(true)
|
in.e.ready := Bool(true)
|
||||||
|
|
||||||
// We need a skidpad to capture D output:
|
// We need a skidpad to capture D output:
|
||||||
// We cannot know if the D response will be accepted until we have
|
// We cannot know if the D response will be accepted until we have
|
||||||
// presented it on D as valid. We also can't back-pressure AHB in the
|
// presented it on D as valid. We also can't back-pressure AHB in the
|
||||||
// data phase. Therefore, we must have enough space to save the data
|
// data phase. Therefore, we must have enough space to save the data
|
||||||
// phase result. Whenever we have a queued response, we can not allow
|
// phase result. Whenever we have a queued response, we can not allow
|
||||||
// AHB to present new responses, so we must quash the address phase.
|
// AHB to present new responses, so we must quash the address phase.
|
||||||
val d = Wire(in.d)
|
val d = Wire(in.d)
|
||||||
in.d <> Queue(d, 1, flow = true)
|
in.d <> Queue(d, 1, flow = true)
|
||||||
val a_quash = in.d.valid && !in.d.ready
|
val a_quash = in.d.valid && !in.d.ready
|
||||||
|
|
||||||
// Record what is coming out in d_phase
|
// Record what is coming out in d_phase
|
||||||
val d_valid = RegInit(Bool(false))
|
val d_valid = RegInit(Bool(false))
|
||||||
val d_hasData = Reg(Bool())
|
val d_hasData = Reg(Bool())
|
||||||
val d_error = Reg(Bool())
|
val d_error = Reg(Bool())
|
||||||
val d_addr_lo = Reg(UInt(width = lgBytes))
|
val d_addr_lo = Reg(UInt(width = lgBytes))
|
||||||
val d_source = Reg(UInt())
|
val d_source = Reg(UInt())
|
||||||
val d_size = Reg(UInt())
|
val d_size = Reg(UInt())
|
||||||
|
|
||||||
when (out.hreadyout) { d_error := d_error || out.hresp }
|
when (out.hreadyout) { d_error := d_error || out.hresp }
|
||||||
when (d.fire()) { d_valid := Bool(false) }
|
when (d.fire()) { d_valid := Bool(false) }
|
||||||
|
|
||||||
d.valid := d_valid && out.hreadyout
|
d.valid := d_valid && out.hreadyout
|
||||||
d.bits := edgeIn.AccessAck(d_addr_lo, UInt(0), d_source, d_size, out.hrdata, out.hresp || d_error)
|
d.bits := edgeIn.AccessAck(d_addr_lo, UInt(0), d_source, d_size, out.hrdata, out.hresp || d_error)
|
||||||
d.bits.opcode := Mux(d_hasData, TLMessages.AccessAckData, TLMessages.AccessAck)
|
d.bits.opcode := Mux(d_hasData, TLMessages.AccessAckData, TLMessages.AccessAck)
|
||||||
|
|
||||||
// We need an irrevocable input for AHB to stall on read bursts
|
// We need an irrevocable input for AHB to stall on read bursts
|
||||||
// We also need the values to NOT change when valid goes low => 1 entry only
|
// We also need the values to NOT change when valid goes low => 1 entry only
|
||||||
val a = Queue(in.a, 1, flow = combinational, pipe = !combinational)
|
val a = Queue(in.a, 1, flow = combinational, pipe = !combinational)
|
||||||
val a_valid = a.valid && !a_quash
|
val a_valid = a.valid && !a_quash
|
||||||
|
|
||||||
// This is lot like TLEdge.firstlast, but counts beats also for single-beat TL types
|
// This is lot like TLEdge.firstlast, but counts beats also for single-beat TL types
|
||||||
val a_size = edgeIn.size(a.bits)
|
val a_size = edgeIn.size(a.bits)
|
||||||
val a_beats1 = UIntToOH1(a_size, lgMax) >> lgBytes
|
val a_beats1 = UIntToOH1(a_size, lgMax) >> lgBytes
|
||||||
val a_counter = RegInit(UInt(0, width = log2Up(maxTransfer/beatBytes)))
|
val a_counter = RegInit(UInt(0, width = log2Up(maxTransfer/beatBytes)))
|
||||||
val a_counter1 = a_counter - UInt(1)
|
val a_counter1 = a_counter - UInt(1)
|
||||||
val a_first = a_counter === UInt(0)
|
val a_first = a_counter === UInt(0)
|
||||||
val a_last = a_counter === UInt(1) || a_beats1 === UInt(0)
|
val a_last = a_counter === UInt(1) || a_beats1 === UInt(0)
|
||||||
val a_offset = (a_beats1 & ~a_counter1) << lgBytes
|
val a_offset = (a_beats1 & ~a_counter1) << lgBytes
|
||||||
val a_hasData = edgeIn.hasData(a.bits)
|
val a_hasData = edgeIn.hasData(a.bits)
|
||||||
|
|
||||||
// Expand no-data A-channel requests into multiple beats
|
// Expand no-data A-channel requests into multiple beats
|
||||||
a.ready := (a_hasData || a_last) && out.hreadyout && !a_quash
|
a.ready := (a_hasData || a_last) && out.hreadyout && !a_quash
|
||||||
when (a_valid && out.hreadyout) {
|
when (a_valid && out.hreadyout) {
|
||||||
a_counter := Mux(a_first, a_beats1, a_counter1)
|
a_counter := Mux(a_first, a_beats1, a_counter1)
|
||||||
d_valid := !a_hasData || a_last
|
d_valid := !a_hasData || a_last
|
||||||
// Record what will be in the data phase
|
// Record what will be in the data phase
|
||||||
when (a_first) {
|
when (a_first) {
|
||||||
d_hasData := !a_hasData
|
d_hasData := !a_hasData
|
||||||
d_error := Bool(false)
|
d_error := Bool(false)
|
||||||
d_addr_lo := a.bits.address
|
d_addr_lo := a.bits.address
|
||||||
d_source := a.bits.source
|
d_source := a.bits.source
|
||||||
d_size := a.bits.size
|
d_size := a.bits.size
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Transform TL size into AHB hsize+hburst
|
||||||
|
val a_size_bits = a_size.getWidth
|
||||||
|
val a_sizeDelta = Cat(UInt(0, width = 1), a_size) - UInt(lgBytes+1)
|
||||||
|
val a_singleBeat = a_sizeDelta(a_size_bits)
|
||||||
|
val a_logBeats1 = a_sizeDelta(a_size_bits-1, 0)
|
||||||
|
|
||||||
|
out.hmastlock := Bool(false) // for now
|
||||||
|
out.htrans := Mux(a_valid, Mux(a_first, TRANS_NONSEQ, TRANS_SEQ), Mux(a_first, TRANS_IDLE, TRANS_BUSY))
|
||||||
|
out.hsel := a_valid || !a_first
|
||||||
|
out.hready := out.hreadyout
|
||||||
|
out.hwrite := a_hasData
|
||||||
|
out.haddr := a.bits.address | a_offset
|
||||||
|
out.hsize := Mux(a_singleBeat, a.bits.size, UInt(lgBytes))
|
||||||
|
out.hburst := Mux(a_singleBeat, BURST_SINGLE, (a_logBeats1<<1) | UInt(1))
|
||||||
|
out.hprot := PROT_DEFAULT
|
||||||
|
out.hwdata := RegEnable(a.bits.data, a.fire())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform TL size into AHB hsize+hburst
|
|
||||||
val a_size_bits = a_size.getWidth
|
|
||||||
val a_sizeDelta = Cat(UInt(0, width = 1), a_size) - UInt(lgBytes+1)
|
|
||||||
val a_singleBeat = a_sizeDelta(a_size_bits)
|
|
||||||
val a_logBeats1 = a_sizeDelta(a_size_bits-1, 0)
|
|
||||||
|
|
||||||
out.hmastlock := Bool(false) // for now
|
|
||||||
out.htrans := Mux(a_valid, Mux(a_first, TRANS_NONSEQ, TRANS_SEQ), Mux(a_first, TRANS_IDLE, TRANS_BUSY))
|
|
||||||
out.hsel := a_valid || !a_first
|
|
||||||
out.hready := out.hreadyout
|
|
||||||
out.hwrite := a_hasData
|
|
||||||
out.haddr := a.bits.address | a_offset
|
|
||||||
out.hsize := Mux(a_singleBeat, a.bits.size, UInt(lgBytes))
|
|
||||||
out.hburst := Mux(a_singleBeat, BURST_SINGLE, (a_logBeats1<<1) | UInt(1))
|
|
||||||
out.hprot := PROT_DEFAULT
|
|
||||||
out.hwdata := RegEnable(a.bits.data, a.fire())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,12 +10,12 @@ import uncore.apb._
|
|||||||
import scala.math.{min, max}
|
import scala.math.{min, max}
|
||||||
import APBParameters._
|
import APBParameters._
|
||||||
|
|
||||||
case class TLToAPBNode() extends MixedNode(TLImp, APBImp)(
|
case class TLToAPBNode() extends MixedAdapterNode(TLImp, APBImp)(
|
||||||
dFn = { case (1, Seq(TLClientPortParameters(clients, unsafeAtomics, minLatency))) =>
|
dFn = { case TLClientPortParameters(clients, unsafeAtomics, minLatency) =>
|
||||||
val masters = clients.map { case c => APBMasterParameters(nodePath = c.nodePath) }
|
val masters = clients.map { case c => APBMasterParameters(nodePath = c.nodePath) }
|
||||||
Seq(APBMasterPortParameters(masters))
|
APBMasterPortParameters(masters)
|
||||||
},
|
},
|
||||||
uFn = { case (1, Seq(APBSlavePortParameters(slaves, beatBytes))) =>
|
uFn = { case APBSlavePortParameters(slaves, beatBytes) =>
|
||||||
val managers = slaves.map { case s =>
|
val managers = slaves.map { case s =>
|
||||||
TLManagerParameters(
|
TLManagerParameters(
|
||||||
address = s.address,
|
address = s.address,
|
||||||
@ -27,10 +27,8 @@ case class TLToAPBNode() extends MixedNode(TLImp, APBImp)(
|
|||||||
supportsPutFull = if (s.supportsWrite) TransferSizes(1, beatBytes) else TransferSizes.none,
|
supportsPutFull = if (s.supportsWrite) TransferSizes(1, beatBytes) else TransferSizes.none,
|
||||||
fifoId = Some(0)) // a common FIFO domain
|
fifoId = Some(0)) // a common FIFO domain
|
||||||
}
|
}
|
||||||
Seq(TLManagerPortParameters(managers, beatBytes, 1, 0))
|
TLManagerPortParameters(managers, beatBytes, 1, 0)
|
||||||
},
|
})
|
||||||
numPO = 1 to 1,
|
|
||||||
numPI = 1 to 1)
|
|
||||||
|
|
||||||
class TLToAPB(combinational: Boolean = true)(implicit p: Parameters) extends LazyModule
|
class TLToAPB(combinational: Boolean = true)(implicit p: Parameters) extends LazyModule
|
||||||
{
|
{
|
||||||
@ -42,51 +40,49 @@ class TLToAPB(combinational: Boolean = true)(implicit p: Parameters) extends Laz
|
|||||||
val out = node.bundleOut
|
val out = node.bundleOut
|
||||||
}
|
}
|
||||||
|
|
||||||
val in = io.in(0)
|
((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
|
||||||
val out = io.out(0)
|
val beatBytes = edgeOut.slave.beatBytes
|
||||||
val edgeIn = node.edgesIn(0)
|
val lgBytes = log2Ceil(beatBytes)
|
||||||
val edgeOut = node.edgesOut(0)
|
|
||||||
val beatBytes = edgeOut.slave.beatBytes
|
|
||||||
val lgBytes = log2Ceil(beatBytes)
|
|
||||||
|
|
||||||
// APB has no cache coherence
|
// APB has no cache coherence
|
||||||
in.b.valid := Bool(false)
|
in.b.valid := Bool(false)
|
||||||
in.c.ready := Bool(true)
|
in.c.ready := Bool(true)
|
||||||
in.e.ready := Bool(true)
|
in.e.ready := Bool(true)
|
||||||
|
|
||||||
// We need a skidpad to capture D output:
|
// We need a skidpad to capture D output:
|
||||||
// We cannot know if the D response will be accepted until we have
|
// We cannot know if the D response will be accepted until we have
|
||||||
// presented it on D as valid. We also can't back-pressure APB in the
|
// presented it on D as valid. We also can't back-pressure APB in the
|
||||||
// data phase. Therefore, we must have enough space to save the data
|
// data phase. Therefore, we must have enough space to save the data
|
||||||
// phase result. Whenever we have a queued response, we can not allow
|
// phase result. Whenever we have a queued response, we can not allow
|
||||||
// APB to present new responses, so we must quash the address phase.
|
// APB to present new responses, so we must quash the address phase.
|
||||||
val d = Wire(in.d)
|
val d = Wire(in.d)
|
||||||
in.d <> Queue(d, 1, flow = true)
|
in.d <> Queue(d, 1, flow = true)
|
||||||
|
|
||||||
// We need an irrevocable input for APB to stall
|
// We need an irrevocable input for APB to stall
|
||||||
val a = Queue(in.a, 1, flow = combinational, pipe = !combinational)
|
val a = Queue(in.a, 1, flow = combinational, pipe = !combinational)
|
||||||
|
|
||||||
val a_enable = RegInit(Bool(false))
|
val a_enable = RegInit(Bool(false))
|
||||||
val a_sel = a.valid && RegNext(!in.d.valid || in.d.ready)
|
val a_sel = a.valid && RegNext(!in.d.valid || in.d.ready)
|
||||||
val a_write = edgeIn.hasData(a.bits)
|
val a_write = edgeIn.hasData(a.bits)
|
||||||
|
|
||||||
when (a_sel) { a_enable := Bool(true) }
|
when (a_sel) { a_enable := Bool(true) }
|
||||||
when (d.fire()) { a_enable := Bool(false) }
|
when (d.fire()) { a_enable := Bool(false) }
|
||||||
|
|
||||||
out.psel := a_sel
|
out.psel := a_sel
|
||||||
out.penable := a_enable
|
out.penable := a_enable
|
||||||
out.pwrite := a_write
|
out.pwrite := a_write
|
||||||
out.paddr := a.bits.address
|
out.paddr := a.bits.address
|
||||||
out.pprot := PROT_DEFAULT
|
out.pprot := PROT_DEFAULT
|
||||||
out.pwdata := a.bits.data
|
out.pwdata := a.bits.data
|
||||||
out.pstrb := Mux(a_write, a.bits.mask, UInt(0))
|
out.pstrb := Mux(a_write, a.bits.mask, UInt(0))
|
||||||
|
|
||||||
a.ready := a_enable && out.pready
|
a.ready := a_enable && out.pready
|
||||||
d.valid := a_enable && out.pready
|
d.valid := a_enable && out.pready
|
||||||
assert (!d.valid || d.ready)
|
assert (!d.valid || d.ready)
|
||||||
|
|
||||||
d.bits := edgeIn.AccessAck(a.bits, UInt(0), out.prdata, out.pslverr)
|
d.bits := edgeIn.AccessAck(a.bits, UInt(0), out.prdata, out.pslverr)
|
||||||
d.bits.opcode := Mux(a_write, TLMessages.AccessAck, TLMessages.AccessAckData)
|
d.bits.opcode := Mux(a_write, TLMessages.AccessAck, TLMessages.AccessAckData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,16 +10,16 @@ import util.PositionalMultiQueue
|
|||||||
import uncore.axi4._
|
import uncore.axi4._
|
||||||
import scala.math.{min, max}
|
import scala.math.{min, max}
|
||||||
|
|
||||||
case class TLToAXI4Node(idBits: Int) extends MixedNode(TLImp, AXI4Imp)(
|
case class TLToAXI4Node(idBits: Int) extends MixedAdapterNode(TLImp, AXI4Imp)(
|
||||||
dFn = { case (1, _) =>
|
dFn = { _ =>
|
||||||
// We must erase all client information, because we crush their source Ids
|
// We must erase all client information, because we crush their source Ids
|
||||||
val masters = Seq(
|
val masters = Seq(
|
||||||
AXI4MasterParameters(
|
AXI4MasterParameters(
|
||||||
id = IdRange(0, 1 << idBits),
|
id = IdRange(0, 1 << idBits),
|
||||||
aligned = true))
|
aligned = true))
|
||||||
Seq(AXI4MasterPortParameters(masters))
|
AXI4MasterPortParameters(masters)
|
||||||
},
|
},
|
||||||
uFn = { case (1, Seq(p)) => Seq(TLManagerPortParameters(
|
uFn = { p => TLManagerPortParameters(
|
||||||
managers = p.slaves.map { case s =>
|
managers = p.slaves.map { case s =>
|
||||||
TLManagerParameters(
|
TLManagerParameters(
|
||||||
address = s.address,
|
address = s.address,
|
||||||
@ -31,10 +31,8 @@ case class TLToAXI4Node(idBits: Int) extends MixedNode(TLImp, AXI4Imp)(
|
|||||||
supportsPutPartial = s.supportsWrite)},
|
supportsPutPartial = s.supportsWrite)},
|
||||||
// AXI4 is NEVER fifo in TL sense (R+W are independent)
|
// AXI4 is NEVER fifo in TL sense (R+W are independent)
|
||||||
beatBytes = p.beatBytes,
|
beatBytes = p.beatBytes,
|
||||||
minLatency = p.minLatency))
|
minLatency = p.minLatency)
|
||||||
},
|
})
|
||||||
numPO = 1 to 1,
|
|
||||||
numPI = 1 to 1)
|
|
||||||
|
|
||||||
class TLToAXI4(idBits: Int, combinational: Boolean = true)(implicit p: Parameters) extends LazyModule
|
class TLToAXI4(idBits: Int, combinational: Boolean = true)(implicit p: Parameters) extends LazyModule
|
||||||
{
|
{
|
||||||
@ -46,185 +44,182 @@ class TLToAXI4(idBits: Int, combinational: Boolean = true)(implicit p: Parameter
|
|||||||
val out = node.bundleOut
|
val out = node.bundleOut
|
||||||
}
|
}
|
||||||
|
|
||||||
val in = io.in(0)
|
((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
|
||||||
val out = io.out(0)
|
val slaves = edgeOut.slave.slaves
|
||||||
|
|
||||||
val edgeIn = node.edgesIn(0)
|
// All pairs of slaves must promise that they will never interleave data
|
||||||
val edgeOut = node.edgesOut(0)
|
require (slaves(0).interleavedId.isDefined)
|
||||||
val slaves = edgeOut.slave.slaves
|
slaves.foreach { s => require (s.interleavedId == slaves(0).interleavedId) }
|
||||||
|
|
||||||
// All pairs of slaves must promise that they will never interleave data
|
// We need to ensure that a slave does not stall trying to send B while we need to receive R
|
||||||
require (slaves(0).interleavedId.isDefined)
|
// Since R&W have independent flow control, it is possible for a W to cut in-line and get into
|
||||||
slaves.foreach { s => require (s.interleavedId == slaves(0).interleavedId) }
|
// a slave's buffers, preventing us from getting all the R responses we need to release D for B.
|
||||||
|
// This risk is compounded by an AXI fragmentation. Even a slave which responds completely to
|
||||||
|
// AR before working on AW might have an AW slipped between two AR fragments.
|
||||||
|
val out_b = Queue.irrevocable(out.b, entries=edgeIn.client.endSourceId, flow=combinational)
|
||||||
|
|
||||||
// We need to ensure that a slave does not stall trying to send B while we need to receive R
|
// We need to keep the following state from A => D: (addr_lo, size, source)
|
||||||
// Since R&W have independent flow control, it is possible for a W to cut in-line and get into
|
// All of those fields could potentially require 0 bits (argh. Chisel.)
|
||||||
// a slave's buffers, preventing us from getting all the R responses we need to release D for B.
|
// We will pack as many of the lowest bits of state as fit into the AXI ID.
|
||||||
// This risk is compounded by an AXI fragmentation. Even a slave which responds completely to
|
// Any bits left-over must be put into a bank of Queues.
|
||||||
// AR before working on AW might have an AW slipped between two AR fragments.
|
// The Queues are indexed by as many of the source bits as fit into the AXI ID.
|
||||||
val out_b = Queue.irrevocable(out.b, entries=edgeIn.client.endSourceId, flow=combinational)
|
// The Queues are deep enough that every source has guaranteed space in its Queue.
|
||||||
|
|
||||||
// We need to keep the following state from A => D: (addr_lo, size, source)
|
val sourceBits = log2Ceil(edgeIn.client.endSourceId)
|
||||||
// All of those fields could potentially require 0 bits (argh. Chisel.)
|
val sizeBits = log2Ceil(edgeIn.maxLgSize+1)
|
||||||
// We will pack as many of the lowest bits of state as fit into the AXI ID.
|
val addrBits = log2Ceil(edgeIn.manager.beatBytes)
|
||||||
// Any bits left-over must be put into a bank of Queues.
|
val stateBits = addrBits + sizeBits + sourceBits // could be 0
|
||||||
// The Queues are indexed by as many of the source bits as fit into the AXI ID.
|
|
||||||
// The Queues are deep enough that every source has guaranteed space in its Queue.
|
|
||||||
|
|
||||||
val sourceBits = log2Ceil(edgeIn.client.endSourceId)
|
val a_address = edgeIn.address(in.a.bits)
|
||||||
val sizeBits = log2Ceil(edgeIn.maxLgSize+1)
|
val a_addr_lo = edgeIn.addr_lo(a_address)
|
||||||
val addrBits = log2Ceil(edgeIn.manager.beatBytes)
|
val a_source = in.a.bits.source
|
||||||
val stateBits = addrBits + sizeBits + sourceBits // could be 0
|
val a_size = edgeIn.size(in.a.bits)
|
||||||
|
val a_isPut = edgeIn.hasData(in.a.bits)
|
||||||
|
val a_last = edgeIn.last(in.a)
|
||||||
|
|
||||||
val a_address = edgeIn.address(in.a.bits)
|
// Make sure the fields are within the bounds we assumed
|
||||||
val a_addr_lo = edgeIn.addr_lo(a_address)
|
assert (a_source < UInt(BigInt(1) << sourceBits))
|
||||||
val a_source = in.a.bits.source
|
assert (a_size < UInt(BigInt(1) << sizeBits))
|
||||||
val a_size = edgeIn.size(in.a.bits)
|
assert (a_addr_lo < UInt(BigInt(1) << addrBits))
|
||||||
val a_isPut = edgeIn.hasData(in.a.bits)
|
|
||||||
val a_last = edgeIn.last(in.a)
|
|
||||||
|
|
||||||
// Make sure the fields are within the bounds we assumed
|
// Carefully pack/unpack fields into the state we send
|
||||||
assert (a_source < UInt(BigInt(1) << sourceBits))
|
val baseEnd = 0
|
||||||
assert (a_size < UInt(BigInt(1) << sizeBits))
|
val (sourceEnd, sourceOff) = (sourceBits + baseEnd, baseEnd)
|
||||||
assert (a_addr_lo < UInt(BigInt(1) << addrBits))
|
val (sizeEnd, sizeOff) = (sizeBits + sourceEnd, sourceEnd)
|
||||||
|
val (addrEnd, addrOff) = (addrBits + sizeEnd, sizeEnd)
|
||||||
|
require (addrEnd == stateBits)
|
||||||
|
|
||||||
// Carefully pack/unpack fields into the state we send
|
val a_state = (a_source << sourceOff) | (a_size << sizeOff) | (a_addr_lo << addrOff)
|
||||||
val baseEnd = 0
|
val a_id = if (idBits == 0) UInt(0) else a_state
|
||||||
val (sourceEnd, sourceOff) = (sourceBits + baseEnd, baseEnd)
|
|
||||||
val (sizeEnd, sizeOff) = (sizeBits + sourceEnd, sourceEnd)
|
|
||||||
val (addrEnd, addrOff) = (addrBits + sizeEnd, sizeEnd)
|
|
||||||
require (addrEnd == stateBits)
|
|
||||||
|
|
||||||
val a_state = (a_source << sourceOff) | (a_size << sizeOff) | (a_addr_lo << addrOff)
|
val r_state = Wire(UInt(width = stateBits))
|
||||||
val a_id = if (idBits == 0) UInt(0) else a_state
|
val r_source = if (sourceBits > 0) r_state(sourceEnd-1, sourceOff) else UInt(0)
|
||||||
|
val r_size = if (sizeBits > 0) r_state(sizeEnd -1, sizeOff) else UInt(0)
|
||||||
|
val r_addr_lo = if (addrBits > 0) r_state(addrEnd -1, addrOff) else UInt(0)
|
||||||
|
|
||||||
val r_state = Wire(UInt(width = stateBits))
|
val b_state = Wire(UInt(width = stateBits))
|
||||||
val r_source = if (sourceBits > 0) r_state(sourceEnd-1, sourceOff) else UInt(0)
|
val b_source = if (sourceBits > 0) b_state(sourceEnd-1, sourceOff) else UInt(0)
|
||||||
val r_size = if (sizeBits > 0) r_state(sizeEnd -1, sizeOff) else UInt(0)
|
val b_size = if (sizeBits > 0) b_state(sizeEnd -1, sizeOff) else UInt(0)
|
||||||
val r_addr_lo = if (addrBits > 0) r_state(addrEnd -1, addrOff) else UInt(0)
|
val b_addr_lo = if (addrBits > 0) b_state(addrEnd -1, addrOff) else UInt(0)
|
||||||
|
|
||||||
val b_state = Wire(UInt(width = stateBits))
|
val r_last = out.r.bits.last
|
||||||
val b_source = if (sourceBits > 0) b_state(sourceEnd-1, sourceOff) else UInt(0)
|
val r_id = out.r.bits.id
|
||||||
val b_size = if (sizeBits > 0) b_state(sizeEnd -1, sizeOff) else UInt(0)
|
val b_id = out_b.bits.id
|
||||||
val b_addr_lo = if (addrBits > 0) b_state(addrEnd -1, addrOff) else UInt(0)
|
|
||||||
|
|
||||||
val r_last = out.r.bits.last
|
if (stateBits <= idBits) { // No need for any state tracking
|
||||||
val r_id = out.r.bits.id
|
r_state := r_id
|
||||||
val b_id = out_b.bits.id
|
b_state := b_id
|
||||||
|
} else {
|
||||||
|
val bankIndexBits = min(sourceBits, idBits)
|
||||||
|
val posBits = max(0, sourceBits - idBits)
|
||||||
|
val implicitBits = max(idBits, sourceBits)
|
||||||
|
val bankBits = stateBits - implicitBits
|
||||||
|
val numBanks = min(1 << bankIndexBits, edgeIn.client.endSourceId)
|
||||||
|
def bankEntries(i: Int) = (edgeIn.client.endSourceId+numBanks-i-1) / numBanks
|
||||||
|
|
||||||
if (stateBits <= idBits) { // No need for any state tracking
|
val banks = Seq.tabulate(numBanks) { i =>
|
||||||
r_state := r_id
|
// We know there can only be as many outstanding requests as TL sources
|
||||||
b_state := b_id
|
// However, AXI read and write queues are not mutually FIFO.
|
||||||
} else {
|
// Therefore, we want to pop them individually, but share the storage.
|
||||||
val bankIndexBits = min(sourceBits, idBits)
|
val bypass = combinational && edgeOut.slave.minLatency == 0
|
||||||
val posBits = max(0, sourceBits - idBits)
|
PositionalMultiQueue(UInt(width=max(1,bankBits)), positions=bankEntries(i), ways=2, combinational=bypass)
|
||||||
val implicitBits = max(idBits, sourceBits)
|
}
|
||||||
val bankBits = stateBits - implicitBits
|
|
||||||
val numBanks = min(1 << bankIndexBits, edgeIn.client.endSourceId)
|
|
||||||
def bankEntries(i: Int) = (edgeIn.client.endSourceId+numBanks-i-1) / numBanks
|
|
||||||
|
|
||||||
val banks = Seq.tabulate(numBanks) { i =>
|
val a_bankPosition = if (posBits == 0) UInt(0) else a_source(sourceBits-1, idBits)
|
||||||
// We know there can only be as many outstanding requests as TL sources
|
val a_bankIndex = if (bankIndexBits == 0) UInt(0) else a_source(bankIndexBits-1, 0)
|
||||||
// However, AXI read and write queues are not mutually FIFO.
|
val r_bankIndex = if (bankIndexBits == 0) UInt(0) else r_id(bankIndexBits-1, 0)
|
||||||
// Therefore, we want to pop them individually, but share the storage.
|
val b_bankIndex = if (bankIndexBits == 0) UInt(0) else b_id(bankIndexBits-1, 0)
|
||||||
val bypass = combinational && edgeOut.slave.minLatency == 0
|
val a_bankSelect = UIntToOH(a_bankIndex, numBanks)
|
||||||
PositionalMultiQueue(UInt(width=max(1,bankBits)), positions=bankEntries(i), ways=2, combinational=bypass)
|
val r_bankSelect = UIntToOH(r_bankIndex, numBanks)
|
||||||
|
val b_bankSelect = UIntToOH(b_bankIndex, numBanks)
|
||||||
|
|
||||||
|
banks.zipWithIndex.foreach { case (q, i) =>
|
||||||
|
// Push a_state into the banks
|
||||||
|
q.io.enq.valid := in.a.fire() && a_last && a_bankSelect(i)
|
||||||
|
q.io.enq.bits.pos := a_bankPosition
|
||||||
|
q.io.enq.bits.data := a_state >> implicitBits
|
||||||
|
q.io.enq.bits.way := Mux(a_isPut, UInt(0), UInt(1))
|
||||||
|
// Pop the bank's ways
|
||||||
|
q.io.deq(0).ready := out_b.fire() && b_bankSelect(i)
|
||||||
|
q.io.deq(1).ready := out.r.fire() && r_bankSelect(i) && r_last
|
||||||
|
// The FIFOs must be valid when we're ready to pop them...
|
||||||
|
assert (q.io.deq(0).valid || !q.io.deq(0).ready)
|
||||||
|
assert (q.io.deq(1).valid || !q.io.deq(1).ready)
|
||||||
|
}
|
||||||
|
|
||||||
|
val b_bankData = Vec(banks.map(_.io.deq(0).bits.data))(b_bankIndex)
|
||||||
|
val b_bankPos = Vec(banks.map(_.io.deq(0).bits.pos ))(b_bankIndex)
|
||||||
|
val r_bankData = Vec(banks.map(_.io.deq(1).bits.data))(r_bankIndex)
|
||||||
|
val r_bankPos = Vec(banks.map(_.io.deq(1).bits.pos ))(r_bankIndex)
|
||||||
|
|
||||||
|
def optCat(x: (Boolean, UInt)*) = { Cat(x.toList.filter(_._1).map(_._2)) }
|
||||||
|
b_state := optCat((bankBits > 0, b_bankData), (posBits > 0, b_bankPos), (idBits > 0, b_id))
|
||||||
|
r_state := optCat((bankBits > 0, r_bankData), (posBits > 0, r_bankPos), (idBits > 0, r_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
val a_bankPosition = if (posBits == 0) UInt(0) else a_source(sourceBits-1, idBits)
|
// We need these Queues because AXI4 queues are irrevocable
|
||||||
val a_bankIndex = if (bankIndexBits == 0) UInt(0) else a_source(bankIndexBits-1, 0)
|
val depth = if (combinational) 1 else 2
|
||||||
val r_bankIndex = if (bankIndexBits == 0) UInt(0) else r_id(bankIndexBits-1, 0)
|
val out_arw = Wire(Decoupled(new AXI4BundleARW(out.params)))
|
||||||
val b_bankIndex = if (bankIndexBits == 0) UInt(0) else b_id(bankIndexBits-1, 0)
|
val out_w = Wire(out.w)
|
||||||
val a_bankSelect = UIntToOH(a_bankIndex, numBanks)
|
out.w <> Queue.irrevocable(out_w, entries=depth, flow=combinational)
|
||||||
val r_bankSelect = UIntToOH(r_bankIndex, numBanks)
|
val queue_arw = Queue.irrevocable(out_arw, entries=depth, flow=combinational)
|
||||||
val b_bankSelect = UIntToOH(b_bankIndex, numBanks)
|
|
||||||
|
|
||||||
banks.zipWithIndex.foreach { case (q, i) =>
|
// Fan out the ARW channel to AR and AW
|
||||||
// Push a_state into the banks
|
out.ar.bits := queue_arw.bits
|
||||||
q.io.enq.valid := in.a.fire() && a_last && a_bankSelect(i)
|
out.aw.bits := queue_arw.bits
|
||||||
q.io.enq.bits.pos := a_bankPosition
|
out.ar.valid := queue_arw.valid && !queue_arw.bits.wen
|
||||||
q.io.enq.bits.data := a_state >> implicitBits
|
out.aw.valid := queue_arw.valid && queue_arw.bits.wen
|
||||||
q.io.enq.bits.way := Mux(a_isPut, UInt(0), UInt(1))
|
queue_arw.ready := Mux(queue_arw.bits.wen, out.aw.ready, out.ar.ready)
|
||||||
// Pop the bank's ways
|
|
||||||
q.io.deq(0).ready := out_b.fire() && b_bankSelect(i)
|
|
||||||
q.io.deq(1).ready := out.r.fire() && r_bankSelect(i) && r_last
|
|
||||||
// The FIFOs must be valid when we're ready to pop them...
|
|
||||||
assert (q.io.deq(0).valid || !q.io.deq(0).ready)
|
|
||||||
assert (q.io.deq(1).valid || !q.io.deq(1).ready)
|
|
||||||
}
|
|
||||||
|
|
||||||
val b_bankData = Vec(banks.map(_.io.deq(0).bits.data))(b_bankIndex)
|
val beatBytes = edgeIn.manager.beatBytes
|
||||||
val b_bankPos = Vec(banks.map(_.io.deq(0).bits.pos ))(b_bankIndex)
|
val maxSize = UInt(log2Ceil(beatBytes))
|
||||||
val r_bankData = Vec(banks.map(_.io.deq(1).bits.data))(r_bankIndex)
|
val doneAW = RegInit(Bool(false))
|
||||||
val r_bankPos = Vec(banks.map(_.io.deq(1).bits.pos ))(r_bankIndex)
|
when (in.a.fire()) { doneAW := !a_last }
|
||||||
|
|
||||||
def optCat(x: (Boolean, UInt)*) = { Cat(x.toList.filter(_._1).map(_._2)) }
|
val arw = out_arw.bits
|
||||||
b_state := optCat((bankBits > 0, b_bankData), (posBits > 0, b_bankPos), (idBits > 0, b_id))
|
arw.wen := a_isPut
|
||||||
r_state := optCat((bankBits > 0, r_bankData), (posBits > 0, r_bankPos), (idBits > 0, r_id))
|
arw.id := a_id // truncated
|
||||||
|
arw.addr := a_address
|
||||||
|
arw.len := UIntToOH1(a_size, AXI4Parameters.lenBits + log2Ceil(beatBytes)) >> log2Ceil(beatBytes)
|
||||||
|
arw.size := Mux(a_size >= maxSize, maxSize, a_size)
|
||||||
|
arw.burst := AXI4Parameters.BURST_INCR
|
||||||
|
arw.lock := UInt(0) // not exclusive (LR/SC unsupported b/c no forward progress guarantee)
|
||||||
|
arw.cache := UInt(0) // do not allow AXI to modify our transactions
|
||||||
|
arw.prot := AXI4Parameters.PROT_PRIVILEDGED
|
||||||
|
arw.qos := UInt(0) // no QoS
|
||||||
|
|
||||||
|
in.a.ready := Mux(a_isPut, (doneAW || out_arw.ready) && out_w.ready, out_arw.ready)
|
||||||
|
out_arw.valid := in.a.valid && Mux(a_isPut, !doneAW && out_w.ready, Bool(true))
|
||||||
|
|
||||||
|
out_w.valid := in.a.valid && a_isPut && (doneAW || out_arw.ready)
|
||||||
|
out_w.bits.data := in.a.bits.data
|
||||||
|
out_w.bits.strb := in.a.bits.mask
|
||||||
|
out_w.bits.last := a_last
|
||||||
|
|
||||||
|
// R and B => D arbitration
|
||||||
|
val r_holds_d = RegInit(Bool(false))
|
||||||
|
when (out.r.fire()) { r_holds_d := !out.r.bits.last }
|
||||||
|
// Give R higher priority than B
|
||||||
|
val r_wins = out.r.valid || r_holds_d
|
||||||
|
|
||||||
|
out.r.ready := in.d.ready
|
||||||
|
out_b.ready := in.d.ready && !r_wins
|
||||||
|
in.d.valid := Mux(r_wins, out.r.valid, out_b.valid)
|
||||||
|
|
||||||
|
val r_error = out.r.bits.resp =/= AXI4Parameters.RESP_OKAY
|
||||||
|
val b_error = out_b.bits.resp =/= AXI4Parameters.RESP_OKAY
|
||||||
|
|
||||||
|
val r_d = edgeIn.AccessAck(r_addr_lo, UInt(0), r_source, r_size, UInt(0), r_error)
|
||||||
|
val b_d = edgeIn.AccessAck(b_addr_lo, UInt(0), b_source, b_size, b_error)
|
||||||
|
|
||||||
|
in.d.bits := Mux(r_wins, r_d, b_d)
|
||||||
|
in.d.bits.data := out.r.bits.data // avoid a costly Mux
|
||||||
|
|
||||||
|
// Tie off unused channels
|
||||||
|
in.b.valid := Bool(false)
|
||||||
|
in.c.ready := Bool(true)
|
||||||
|
in.e.ready := Bool(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need these Queues because AXI4 queues are irrevocable
|
|
||||||
val depth = if (combinational) 1 else 2
|
|
||||||
val out_arw = Wire(Decoupled(new AXI4BundleARW(out.params)))
|
|
||||||
val out_w = Wire(out.w)
|
|
||||||
out.w <> Queue.irrevocable(out_w, entries=depth, flow=combinational)
|
|
||||||
val queue_arw = Queue.irrevocable(out_arw, entries=depth, flow=combinational)
|
|
||||||
|
|
||||||
// Fan out the ARW channel to AR and AW
|
|
||||||
out.ar.bits := queue_arw.bits
|
|
||||||
out.aw.bits := queue_arw.bits
|
|
||||||
out.ar.valid := queue_arw.valid && !queue_arw.bits.wen
|
|
||||||
out.aw.valid := queue_arw.valid && queue_arw.bits.wen
|
|
||||||
queue_arw.ready := Mux(queue_arw.bits.wen, out.aw.ready, out.ar.ready)
|
|
||||||
|
|
||||||
val beatBytes = edgeIn.manager.beatBytes
|
|
||||||
val maxSize = UInt(log2Ceil(beatBytes))
|
|
||||||
val doneAW = RegInit(Bool(false))
|
|
||||||
when (in.a.fire()) { doneAW := !a_last }
|
|
||||||
|
|
||||||
val arw = out_arw.bits
|
|
||||||
arw.wen := a_isPut
|
|
||||||
arw.id := a_id // truncated
|
|
||||||
arw.addr := a_address
|
|
||||||
arw.len := UIntToOH1(a_size, AXI4Parameters.lenBits + log2Ceil(beatBytes)) >> log2Ceil(beatBytes)
|
|
||||||
arw.size := Mux(a_size >= maxSize, maxSize, a_size)
|
|
||||||
arw.burst := AXI4Parameters.BURST_INCR
|
|
||||||
arw.lock := UInt(0) // not exclusive (LR/SC unsupported b/c no forward progress guarantee)
|
|
||||||
arw.cache := UInt(0) // do not allow AXI to modify our transactions
|
|
||||||
arw.prot := AXI4Parameters.PROT_PRIVILEDGED
|
|
||||||
arw.qos := UInt(0) // no QoS
|
|
||||||
|
|
||||||
in.a.ready := Mux(a_isPut, (doneAW || out_arw.ready) && out_w.ready, out_arw.ready)
|
|
||||||
out_arw.valid := in.a.valid && Mux(a_isPut, !doneAW && out_w.ready, Bool(true))
|
|
||||||
|
|
||||||
out_w.valid := in.a.valid && a_isPut && (doneAW || out_arw.ready)
|
|
||||||
out_w.bits.data := in.a.bits.data
|
|
||||||
out_w.bits.strb := in.a.bits.mask
|
|
||||||
out_w.bits.last := a_last
|
|
||||||
|
|
||||||
// R and B => D arbitration
|
|
||||||
val r_holds_d = RegInit(Bool(false))
|
|
||||||
when (out.r.fire()) { r_holds_d := !out.r.bits.last }
|
|
||||||
// Give R higher priority than B
|
|
||||||
val r_wins = out.r.valid || r_holds_d
|
|
||||||
|
|
||||||
out.r.ready := in.d.ready
|
|
||||||
out_b.ready := in.d.ready && !r_wins
|
|
||||||
in.d.valid := Mux(r_wins, out.r.valid, out_b.valid)
|
|
||||||
|
|
||||||
val r_error = out.r.bits.resp =/= AXI4Parameters.RESP_OKAY
|
|
||||||
val b_error = out_b.bits.resp =/= AXI4Parameters.RESP_OKAY
|
|
||||||
|
|
||||||
val r_d = edgeIn.AccessAck(r_addr_lo, UInt(0), r_source, r_size, UInt(0), r_error)
|
|
||||||
val b_d = edgeIn.AccessAck(b_addr_lo, UInt(0), b_source, b_size, b_error)
|
|
||||||
|
|
||||||
in.d.bits := Mux(r_wins, r_d, b_d)
|
|
||||||
in.d.bits.data := out.r.bits.data // avoid a costly Mux
|
|
||||||
|
|
||||||
// Tie off unused channels
|
|
||||||
in.b.valid := Bool(false)
|
|
||||||
in.c.ready := Bool(true)
|
|
||||||
in.e.ready := Bool(true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,8 +12,8 @@ import scala.math.{min,max}
|
|||||||
class TLWidthWidget(innerBeatBytes: Int)(implicit p: Parameters) extends LazyModule
|
class TLWidthWidget(innerBeatBytes: Int)(implicit p: Parameters) extends LazyModule
|
||||||
{
|
{
|
||||||
val node = TLAdapterNode(
|
val node = TLAdapterNode(
|
||||||
clientFn = { case Seq(c) => c },
|
clientFn = { case c => c },
|
||||||
managerFn = { case Seq(m) => m.copy(beatBytes = innerBeatBytes) })
|
managerFn = { case m => m.copy(beatBytes = innerBeatBytes) })
|
||||||
|
|
||||||
lazy val module = new LazyModuleImp(this) {
|
lazy val module = new LazyModuleImp(this) {
|
||||||
val io = new Bundle {
|
val io = new Bundle {
|
||||||
@ -139,27 +139,24 @@ class TLWidthWidget(innerBeatBytes: Int)(implicit p: Parameters) extends LazyMod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val edgeOut = node.edgesOut(0)
|
((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
|
||||||
val edgeIn = node.edgesIn(0)
|
splice(edgeIn, in.a, edgeOut, out.a)
|
||||||
val in = io.in(0)
|
splice(edgeOut, out.d, edgeIn, in.d)
|
||||||
val out = io.out(0)
|
|
||||||
|
|
||||||
splice(edgeIn, in.a, edgeOut, out.a)
|
if (edgeOut.manager.anySupportAcquireB && edgeIn.client.anySupportProbe) {
|
||||||
splice(edgeOut, out.d, edgeIn, in.d)
|
splice(edgeOut, out.b, edgeIn, in.b)
|
||||||
|
splice(edgeIn, in.c, edgeOut, out.c)
|
||||||
if (edgeOut.manager.anySupportAcquireB && edgeIn.client.anySupportProbe) {
|
in.e.ready := out.e.ready
|
||||||
splice(edgeOut, out.b, edgeIn, in.b)
|
out.e.valid := in.e.valid
|
||||||
splice(edgeIn, in.c, edgeOut, out.c)
|
out.e.bits := in.e.bits
|
||||||
in.e.ready := out.e.ready
|
} else {
|
||||||
out.e.valid := in.e.valid
|
in.b.valid := Bool(false)
|
||||||
out.e.bits := in.e.bits
|
in.c.ready := Bool(true)
|
||||||
} else {
|
in.e.ready := Bool(true)
|
||||||
in.b.valid := Bool(false)
|
out.b.ready := Bool(true)
|
||||||
in.c.ready := Bool(true)
|
out.c.valid := Bool(false)
|
||||||
in.e.ready := Bool(true)
|
out.e.valid := Bool(false)
|
||||||
out.b.ready := Bool(true)
|
}
|
||||||
out.c.valid := Bool(false)
|
|
||||||
out.e.valid := Bool(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ class TLXbar(policy: TLArbiter.Policy = TLArbiter.lowestIndexFirst)(implicit p:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val node = TLAdapterNode(
|
val node = TLNexusNode(
|
||||||
numClientPorts = 1 to 32,
|
numClientPorts = 1 to 32,
|
||||||
numManagerPorts = 1 to 32,
|
numManagerPorts = 1 to 32,
|
||||||
clientFn = { seq =>
|
clientFn = { seq =>
|
||||||
|
Loading…
Reference in New Issue
Block a user