Fragmenter: add an option for earlyAck only on PutFulls (#1095)
Fragmenter: add a third case for earlyAck (PutFulls only) It seems quite common to have a device that is backed by ECC. When performing a multibeat PutPartial, these devices can exhibit their first error on the last beat (if it had an incomplete write mask for that beat, which required read-write-modifying corrupted data). Generally, these devices have ECC granularity <= the bus width. In those cases, if you send a PutFull, the first beat carries the error value for the whole burst. Consider: If the PutFull was below the granularity, it was a single beat. If the PutFull was multi-beat, it exceeds the granularity. Therefore, an important variation on the earlyAck optimization is the case where only PutFulls receive an earlyAck.
This commit is contained in:
parent
4514adb77c
commit
b59880fe8e
@ -122,7 +122,7 @@ trait CanHaveScratchpad extends HasHellaCache with HasICacheFrontend {
|
|||||||
val xbar = LazyModule(new TLXbar)
|
val xbar = LazyModule(new TLXbar)
|
||||||
xbar.node := slaveNode
|
xbar.node := slaveNode
|
||||||
xbarPorts.foreach { case (port, bytes) =>
|
xbarPorts.foreach { case (port, bytes) =>
|
||||||
(Seq(port, TLFragmenter(bytes, cacheBlockBytes, earlyAck=true))
|
(Seq(port, TLFragmenter(bytes, cacheBlockBytes, earlyAck=EarlyAck.PutFulls))
|
||||||
++ (xBytes != bytes).option(TLWidthWidget(xBytes)))
|
++ (xBytes != bytes).option(TLWidthWidget(xBytes)))
|
||||||
.foldRight(xbar.node:TLOutwardNode)(_ := _)
|
.foldRight(xbar.node:TLOutwardNode)(_ := _)
|
||||||
}
|
}
|
||||||
|
@ -8,21 +8,29 @@ import freechips.rocketchip.diplomacy._
|
|||||||
import freechips.rocketchip.util._
|
import freechips.rocketchip.util._
|
||||||
import scala.math.{min,max}
|
import scala.math.{min,max}
|
||||||
|
|
||||||
|
object EarlyAck {
|
||||||
|
sealed trait T
|
||||||
|
case object AllPuts extends T
|
||||||
|
case object PutFulls extends T
|
||||||
|
case object None extends T
|
||||||
|
}
|
||||||
|
|
||||||
// minSize: minimum size of transfers supported by all outward managers
|
// minSize: minimum size of transfers supported by all outward managers
|
||||||
// maxSize: maximum size of transfers supported after the Fragmenter is applied
|
// maxSize: maximum size of transfers supported after the Fragmenter is applied
|
||||||
// alwaysMin: fragment all requests down to minSize (else fragment to maximum supported by manager)
|
// alwaysMin: fragment all requests down to minSize (else fragment to maximum supported by manager)
|
||||||
// Fragmenter modifies: PutFull, PutPartial, LogicalData, Get, Hint
|
// Fragmenter modifies: PutFull, PutPartial, LogicalData, Get, Hint
|
||||||
// Fragmenter passes: ArithmeticData (truncated to minSize if alwaysMin)
|
// Fragmenter passes: ArithmeticData (truncated to minSize if alwaysMin)
|
||||||
// Fragmenter cannot modify acquire (could livelock); thus it is unsafe to put caches on both sides
|
// Fragmenter cannot modify acquire (could livelock); thus it is unsafe to put caches on both sides
|
||||||
class TLFragmenter(val minSize: Int, val maxSize: Int, val alwaysMin: Boolean = false, val earlyAck: Boolean = false)(implicit p: Parameters) extends LazyModule
|
class TLFragmenter(val minSize: Int, val maxSize: Int, val alwaysMin: Boolean = false, val earlyAck: EarlyAck.T = EarlyAck.None)(implicit p: Parameters) extends LazyModule
|
||||||
{
|
{
|
||||||
require (isPow2 (maxSize))
|
require (isPow2 (maxSize))
|
||||||
require (isPow2 (minSize))
|
require (isPow2 (minSize))
|
||||||
require (minSize < maxSize)
|
require (minSize < maxSize)
|
||||||
|
|
||||||
val fragmentBits = log2Ceil(maxSize / minSize)
|
val fragmentBits = log2Ceil(maxSize / minSize)
|
||||||
|
val fullBits = if (earlyAck == EarlyAck.PutFulls) 1 else 0
|
||||||
val toggleBits = 1
|
val toggleBits = 1
|
||||||
val addedBits = fragmentBits + toggleBits
|
val addedBits = fragmentBits + toggleBits + fullBits
|
||||||
|
|
||||||
def expandTransfer(x: TransferSizes) = if (!x) x else {
|
def expandTransfer(x: TransferSizes) = if (!x) x else {
|
||||||
// validate that we can apply the fragmenter correctly
|
// validate that we can apply the fragmenter correctly
|
||||||
@ -179,7 +187,12 @@ class TLFragmenter(val minSize: Int, val maxSize: Int, val alwaysMin: Boolean =
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Swallow up non-data ack fragments
|
// Swallow up non-data ack fragments
|
||||||
val drop = !dHasData && !(if (earlyAck) dFirst else dLast)
|
val doEarlyAck = earlyAck match {
|
||||||
|
case EarlyAck.AllPuts => Bool(true)
|
||||||
|
case EarlyAck.PutFulls => out.d.bits.source(fragmentBits+1)
|
||||||
|
case EarlyAck.None => Bool(false)
|
||||||
|
}
|
||||||
|
val drop = !dHasData && !Mux(doEarlyAck, dFirst, dLast)
|
||||||
out.d.ready := in.d.ready || drop
|
out.d.ready := in.d.ready || drop
|
||||||
in.d.valid := out.d.valid && !drop
|
in.d.valid := out.d.valid && !drop
|
||||||
in.d.bits := out.d.bits // pass most stuff unchanged
|
in.d.bits := out.d.bits // pass most stuff unchanged
|
||||||
@ -193,12 +206,8 @@ class TLFragmenter(val minSize: Int, val maxSize: Int, val alwaysMin: Boolean =
|
|||||||
when (out.d.fire()) { r_error := d_error }
|
when (out.d.fire()) { r_error := d_error }
|
||||||
in.d.bits.error := d_error
|
in.d.bits.error := d_error
|
||||||
|
|
||||||
if (earlyAck) {
|
|
||||||
// If you do early Ack, errors may not be dropped
|
// If you do early Ack, errors may not be dropped
|
||||||
// ... which roughly means: Puts must error on the first burst
|
assert (!out.d.valid || !doEarlyAck || !drop || out.d.bits.error === r_error, "Slave device error behaviour unsuitable for earlyAck setting")
|
||||||
// (dPut && !dFirst) => d.error === r_error
|
|
||||||
assert (!out.d.valid || !dHasData || dFirst || out.d.bits.error === r_error, "Slave device error behaviour unsuitable for earlyAck")
|
|
||||||
}
|
|
||||||
|
|
||||||
// What maximum transfer sizes do downstream devices support?
|
// What maximum transfer sizes do downstream devices support?
|
||||||
val maxArithmetics = managers.map(_.supportsArithmetic.max)
|
val maxArithmetics = managers.map(_.supportsArithmetic.max)
|
||||||
@ -254,13 +263,14 @@ class TLFragmenter(val minSize: Int, val maxSize: Int, val alwaysMin: Boolean =
|
|||||||
val aFragnum = ~(~(old_gennum1 >> log2Ceil(minSize/beatBytes)) | (aFragOH1 >> log2Ceil(minSize)))
|
val aFragnum = ~(~(old_gennum1 >> log2Ceil(minSize/beatBytes)) | (aFragOH1 >> log2Ceil(minSize)))
|
||||||
val aLast = aFragnum === UInt(0)
|
val aLast = aFragnum === UInt(0)
|
||||||
val aToggle = !Mux(aFirst, dToggle, RegEnable(dToggle, aFirst))
|
val aToggle = !Mux(aFirst, dToggle, RegEnable(dToggle, aFirst))
|
||||||
|
val aFull = if (earlyAck == EarlyAck.PutFulls) Some(in_a.bits.opcode === TLMessages.PutFullData) else None
|
||||||
|
|
||||||
when (out.a.fire()) { gennum := new_gennum }
|
when (out.a.fire()) { gennum := new_gennum }
|
||||||
|
|
||||||
repeater.io.repeat := !aHasData && aFragnum =/= UInt(0)
|
repeater.io.repeat := !aHasData && aFragnum =/= UInt(0)
|
||||||
out.a <> in_a
|
out.a <> in_a
|
||||||
out.a.bits.address := in_a.bits.address | ~(old_gennum1 << log2Ceil(beatBytes) | ~aOrigOH1 | aFragOH1 | UInt(minSize-1))
|
out.a.bits.address := in_a.bits.address | ~(old_gennum1 << log2Ceil(beatBytes) | ~aOrigOH1 | aFragOH1 | UInt(minSize-1))
|
||||||
out.a.bits.source := Cat(Seq(in_a.bits.source) ++ Seq(aToggle.asUInt, aFragnum))
|
out.a.bits.source := Cat(Seq(in_a.bits.source) ++ aFull ++ Seq(aToggle.asUInt, aFragnum))
|
||||||
out.a.bits.size := aFrag
|
out.a.bits.size := aFrag
|
||||||
|
|
||||||
// Optimize away some of the Repeater's registers
|
// Optimize away some of the Repeater's registers
|
||||||
@ -283,7 +293,7 @@ class TLFragmenter(val minSize: Int, val maxSize: Int, val alwaysMin: Boolean =
|
|||||||
|
|
||||||
object TLFragmenter
|
object TLFragmenter
|
||||||
{
|
{
|
||||||
def apply(minSize: Int, maxSize: Int, alwaysMin: Boolean = false, earlyAck: Boolean = false)(implicit p: Parameters): TLNode =
|
def apply(minSize: Int, maxSize: Int, alwaysMin: Boolean = false, earlyAck: EarlyAck.T = EarlyAck.None)(implicit p: Parameters): TLNode =
|
||||||
LazyModule(new TLFragmenter(minSize, maxSize, alwaysMin, earlyAck)).node
|
LazyModule(new TLFragmenter(minSize, maxSize, alwaysMin, earlyAck)).node
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,7 +309,7 @@ class TLRAMFragmenter(ramBeatBytes: Int, maxSize: Int, txns: Int)(implicit p: Pa
|
|||||||
:= TLDelayer(0.1)
|
:= TLDelayer(0.1)
|
||||||
:= TLBuffer(BufferParams.flow)
|
:= TLBuffer(BufferParams.flow)
|
||||||
:= TLDelayer(0.1)
|
:= TLDelayer(0.1)
|
||||||
:= TLFragmenter(ramBeatBytes, maxSize, earlyAck = true)
|
:= TLFragmenter(ramBeatBytes, maxSize, earlyAck = EarlyAck.AllPuts)
|
||||||
:= TLDelayer(0.1)
|
:= TLDelayer(0.1)
|
||||||
:= TLBuffer(BufferParams.flow)
|
:= TLBuffer(BufferParams.flow)
|
||||||
:= TLFragmenter(ramBeatBytes, maxSize/2)
|
:= TLFragmenter(ramBeatBytes, maxSize/2)
|
||||||
|
Loading…
Reference in New Issue
Block a user