2017-01-18 03:52:47 +01:00
|
|
|
// See LICENSE.SiFive for license details.
|
|
|
|
|
2017-07-07 19:48:16 +02:00
|
|
|
package freechips.rocketchip.tilelink
|
2017-01-18 03:52:47 +01:00
|
|
|
|
|
|
|
import Chisel._
|
|
|
|
import chisel3.internal.sourceinfo.SourceInfo
|
2017-07-07 19:48:16 +02:00
|
|
|
import freechips.rocketchip.config.Parameters
|
|
|
|
import freechips.rocketchip.diplomacy._
|
2017-01-18 03:52:47 +01:00
|
|
|
import scala.math.{min,max}
|
|
|
|
import TLMessages._
|
|
|
|
|
|
|
|
class TLCacheCork(unsafe: Boolean = false)(implicit p: Parameters) extends LazyModule
|
|
|
|
{
|
|
|
|
val node = TLAdapterNode(
|
2017-01-30 00:17:52 +01:00
|
|
|
clientFn = { case cp =>
|
2017-01-18 03:52:47 +01:00
|
|
|
cp.copy(clients = cp.clients.map { c => c.copy(
|
2017-04-11 21:34:18 +02:00
|
|
|
supportsProbe = TransferSizes.none,
|
2017-01-18 03:52:47 +01:00
|
|
|
sourceId = IdRange(c.sourceId.start*2, c.sourceId.end*2))})},
|
2017-01-30 00:17:52 +01:00
|
|
|
managerFn = { case mp =>
|
2017-03-24 02:19:04 +01:00
|
|
|
mp.copy(
|
|
|
|
endSinkId = 1,
|
|
|
|
managers = mp.managers.map { m => m.copy(
|
2017-07-27 09:25:07 +02:00
|
|
|
supportsAcquireB = if (m.regionType == RegionType.UNCACHED) m.supportsGet else m.supportsAcquireB,
|
|
|
|
supportsAcquireT = if (m.regionType == RegionType.UNCACHED) m.supportsPutFull else m.supportsAcquireT)})})
|
2017-01-18 03:52:47 +01:00
|
|
|
|
|
|
|
lazy val module = new LazyModuleImp(this) {
|
|
|
|
val io = new Bundle {
|
|
|
|
val in = node.bundleIn
|
|
|
|
val out = node.bundleOut
|
|
|
|
}
|
|
|
|
|
2017-01-30 00:17:52 +01:00
|
|
|
((io.in zip io.out) zip (node.edgesIn zip node.edgesOut)) foreach { case ((in, out), (edgeIn, edgeOut)) =>
|
2017-02-09 22:59:09 +01:00
|
|
|
val clients = edgeIn.client.clients
|
|
|
|
val caches = clients.filter(_.supportsProbe)
|
|
|
|
require (clients.size == 1 || caches.size == 0 || unsafe, "Only one client can safely use a TLCacheCork")
|
2017-05-23 04:37:11 +02:00
|
|
|
require (caches.size <= 1 || unsafe, "Only one caching client allowed")
|
2017-01-30 00:17:52 +01:00
|
|
|
edgeOut.manager.managers.foreach { case m =>
|
2017-05-23 04:37:11 +02:00
|
|
|
require (!m.supportsAcquireB || unsafe, "Cannot support caches beyond the Cork")
|
2017-07-27 09:25:07 +02:00
|
|
|
require (m.regionType <= RegionType.UNCACHED)
|
2017-01-30 00:17:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
2017-07-27 23:07:24 +02:00
|
|
|
// Take requests from A to A or D (if BtoT Acquire)
|
2017-01-30 00:17:52 +01:00
|
|
|
val a_a = Wire(out.a)
|
2017-07-27 23:07:24 +02:00
|
|
|
val a_d = Wire(in.d)
|
|
|
|
val isPut = in.a.bits.opcode === PutFullData || in.a.bits.opcode === PutPartialData
|
|
|
|
val toD = in.a.bits.opcode === Acquire && in.a.bits.param === TLPermissions.BtoT
|
|
|
|
in.a.ready := Mux(toD, a_d.ready, a_a.ready)
|
|
|
|
|
|
|
|
a_a.valid := in.a.valid && !toD
|
|
|
|
a_a.bits := in.a.bits
|
2017-01-30 00:17:52 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2017-07-27 23:07:24 +02:00
|
|
|
// Upgrades are instantly successful
|
|
|
|
a_d.valid := in.a.valid && toD
|
|
|
|
a_d.bits.opcode := Grant
|
|
|
|
a_d.bits.param := TLPermissions.toT
|
|
|
|
a_d.bits.size := in.a.bits.size
|
|
|
|
a_d.bits.source := in.a.bits.source
|
|
|
|
a_d.bits.sink := UInt(0)
|
|
|
|
a_d.bits.data := UInt(0)
|
|
|
|
a_d.bits.error := Bool(false)
|
|
|
|
|
2017-01-30 00:17:52 +01:00
|
|
|
// 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
|
|
|
|
|
2017-07-27 23:07:24 +02:00
|
|
|
// Releases without Data succeed instantly
|
2017-01-30 00:17:52 +01:00
|
|
|
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.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
|
2017-07-27 23:07:24 +02:00
|
|
|
// On Grant error, you do NOT get the permissions you asked for.
|
|
|
|
// We only enter this case from NtoT or NtoB, so that means use toN.
|
|
|
|
// (the BtoT case was handled by a_d)
|
|
|
|
d_d.bits.param := Mux(out.d.bits.error, TLPermissions.toN, TLPermissions.toT)
|
2017-01-30 00:17:52 +01:00
|
|
|
}
|
|
|
|
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))
|
2017-07-27 23:07:24 +02:00
|
|
|
TLArbiter(TLArbiter.lowestIndexFirst)(in.d, (edgeIn .numBeats1(d_d.bits), d_d), (UInt(0), Queue(c_d, 2)), (UInt(0), Queue(a_d, 2)))
|
2017-01-30 00:17:52 +01:00
|
|
|
|
|
|
|
// Tie off unused ports
|
|
|
|
in.b.valid := Bool(false)
|
|
|
|
out.c.valid := Bool(false)
|
|
|
|
out.e.valid := Bool(false)
|
2017-01-18 03:52:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
object TLCacheCork
|
|
|
|
{
|
|
|
|
// applied to the TL source node; y.node := TLCacheCork()(x.node)
|
|
|
|
def apply(unsafe: Boolean = false)(x: TLOutwardNode)(implicit p: Parameters, sourceInfo: SourceInfo): TLOutwardNode = {
|
|
|
|
val cork = LazyModule(new TLCacheCork(unsafe))
|
|
|
|
cork.node := x
|
|
|
|
cork.node
|
|
|
|
}
|
|
|
|
}
|