203 lines
7.0 KiB
Scala
203 lines
7.0 KiB
Scala
// See LICENSE for license details.
|
|
|
|
package uncore.agents
|
|
|
|
import Chisel._
|
|
import uncore.coherence._
|
|
import uncore.tilelink._
|
|
import uncore.constants._
|
|
import cde.Parameters
|
|
|
|
class L2BroadcastHub(implicit p: Parameters) extends HierarchicalCoherenceAgent()(p) {
|
|
|
|
// Create TSHRs for outstanding transactions
|
|
val irelTrackerList =
|
|
(0 until nReleaseTransactors).map(id =>
|
|
Module(new BufferedBroadcastVoluntaryReleaseTracker(id)))
|
|
val iacqTrackerList =
|
|
(nReleaseTransactors until nTransactors).map(id =>
|
|
Module(new BufferedBroadcastAcquireTracker(id)))
|
|
val trackerList = irelTrackerList ++ iacqTrackerList
|
|
|
|
// Propagate incoherence flags
|
|
trackerList.map(_.io.incoherent) foreach { _ := io.incoherent }
|
|
|
|
// Create an arbiter for the one memory port
|
|
val outerList = trackerList.map(_.io.outer)
|
|
val outer_arb = Module(new ClientTileLinkIOArbiter(outerList.size)
|
|
(p.alterPartial({ case TLId => p(OuterTLId) })))
|
|
outer_arb.io.in <> outerList
|
|
io.outer <> outer_arb.io.out
|
|
|
|
// Handle acquire transaction initiation
|
|
val irel_vs_iacq_conflict =
|
|
io.inner.acquire.valid &&
|
|
io.inner.release.valid &&
|
|
io.irel().conflicts(io.iacq())
|
|
|
|
doInputRoutingWithAllocation(
|
|
in = io.inner.acquire,
|
|
outs = trackerList.map(_.io.inner.acquire),
|
|
allocs = trackerList.map(_.io.alloc.iacq),
|
|
allocOverride = Some(!irel_vs_iacq_conflict))
|
|
|
|
// Handle releases, which might be voluntary and might have data
|
|
doInputRoutingWithAllocation(
|
|
in = io.inner.release,
|
|
outs = trackerList.map(_.io.inner.release),
|
|
allocs = trackerList.map(_.io.alloc.irel))
|
|
|
|
// Wire probe requests and grant reply to clients, finish acks from clients
|
|
doOutputArbitration(io.inner.probe, trackerList.map(_.io.inner.probe))
|
|
|
|
doOutputArbitration(io.inner.grant, trackerList.map(_.io.inner.grant))
|
|
|
|
doInputRouting(io.inner.finish, trackerList.map(_.io.inner.finish))
|
|
}
|
|
|
|
class BroadcastXactTracker(implicit p: Parameters) extends XactTracker()(p) {
|
|
val io = new HierarchicalXactTrackerIO
|
|
pinAllReadyValidLow(io)
|
|
}
|
|
|
|
trait BroadcastsToAllClients extends HasCoherenceAgentParameters {
|
|
val coh = HierarchicalMetadata.onReset
|
|
val inner_coh = coh.inner
|
|
val outer_coh = coh.outer
|
|
def full_representation = ~UInt(0, width = innerNCachingClients)
|
|
}
|
|
|
|
abstract class BroadcastVoluntaryReleaseTracker(trackerId: Int)(implicit p: Parameters)
|
|
extends VoluntaryReleaseTracker(trackerId)(p)
|
|
with EmitsVoluntaryReleases
|
|
with BroadcastsToAllClients {
|
|
val io = new HierarchicalXactTrackerIO
|
|
pinAllReadyValidLow(io)
|
|
|
|
// Checks for illegal behavior
|
|
assert(!(state === s_idle && io.inner.release.fire() && io.alloc.irel.should && !io.irel().isVoluntary()),
|
|
"VoluntaryReleaseTracker accepted Release that wasn't voluntary!")
|
|
}
|
|
|
|
abstract class BroadcastAcquireTracker(trackerId: Int)(implicit p: Parameters)
|
|
extends AcquireTracker(trackerId)(p)
|
|
with EmitsVoluntaryReleases
|
|
with BroadcastsToAllClients {
|
|
val io = new HierarchicalXactTrackerIO
|
|
pinAllReadyValidLow(io)
|
|
|
|
val alwaysWriteFullBeat = false
|
|
val nSecondaryMisses = 1
|
|
def iacq_can_merge = Bool(false)
|
|
|
|
// Checks for illegal behavior
|
|
// TODO: this could be allowed, but is a useful check against allocation gone wild
|
|
assert(!(state === s_idle && io.inner.acquire.fire() && io.alloc.iacq.should &&
|
|
io.iacq().hasMultibeatData() && !io.iacq().first()),
|
|
"AcquireTracker initialized with a tail data beat.")
|
|
|
|
assert(!(state =/= s_idle && pending_ignt && xact_iacq.isPrefetch()),
|
|
"Broadcast Hub does not support Prefetches.")
|
|
|
|
assert(!(state =/= s_idle && pending_ignt && xact_iacq.isAtomic()),
|
|
"Broadcast Hub does not support PutAtomics.")
|
|
}
|
|
|
|
class BufferedBroadcastVoluntaryReleaseTracker(trackerId: Int)(implicit p: Parameters)
|
|
extends BroadcastVoluntaryReleaseTracker(trackerId)(p)
|
|
with HasDataBuffer {
|
|
|
|
// Tell the parent if any incoming messages conflict with the ongoing transaction
|
|
routeInParent()
|
|
io.alloc.iacq.can := Bool(false)
|
|
|
|
// Start transaction by accepting inner release
|
|
innerRelease(block_vol_ignt = pending_orel || vol_ognt_counter.pending)
|
|
|
|
// A release beat can be accepted if we are idle, if its a mergeable transaction, or if its a tail beat
|
|
io.inner.release.ready := state === s_idle || irel_can_merge || irel_same_xact
|
|
|
|
when(io.inner.release.fire()) { data_buffer(io.irel().addr_beat) := io.irel().data }
|
|
|
|
// Dispatch outer release
|
|
outerRelease(
|
|
coh = outer_coh.onHit(M_XWR),
|
|
data = data_buffer(vol_ognt_counter.up.idx),
|
|
add_pending_send_bit = irel_is_allocating)
|
|
|
|
quiesce() {}
|
|
}
|
|
|
|
class BufferedBroadcastAcquireTracker(trackerId: Int)(implicit p: Parameters)
|
|
extends BroadcastAcquireTracker(trackerId)(p)
|
|
with HasByteWriteMaskBuffer {
|
|
|
|
// Setup IOs used for routing in the parent
|
|
routeInParent()
|
|
io.alloc.irel.can := Bool(false)
|
|
|
|
// First, take care of accpeting new acquires or secondary misses
|
|
// Handling of primary and secondary misses' data and write mask merging
|
|
innerAcquire(
|
|
can_alloc = Bool(false),
|
|
next = s_inner_probe)
|
|
|
|
io.inner.acquire.ready := state === s_idle || iacq_can_merge || iacq_same_xact_multibeat
|
|
|
|
// Track which clients yet need to be probed and make Probe message
|
|
// If a writeback occurs, we can forward its data via the buffer,
|
|
// and skip having to go outwards
|
|
val skip_outer_acquire = pending_ignt_data.andR
|
|
|
|
innerProbe(
|
|
inner_coh.makeProbe(curr_probe_dst, xact_iacq, xact_addr_block),
|
|
Mux(!skip_outer_acquire, s_outer_acquire, s_busy))
|
|
|
|
// Handle incoming releases from clients, which may reduce sharer counts
|
|
// and/or write back dirty data, and may be unexpected voluntary releases
|
|
def irel_can_merge = io.irel().conflicts(xact_addr_block) &&
|
|
io.irel().isVoluntary() &&
|
|
!Vec(s_idle, s_meta_write).contains(state) &&
|
|
!all_pending_done &&
|
|
!io.outer.grant.fire() &&
|
|
!io.inner.grant.fire() &&
|
|
!vol_ignt_counter.pending
|
|
|
|
innerRelease(block_vol_ignt = vol_ognt_counter.pending)
|
|
|
|
//TODO: accept vol irels when state === s_idle, operate like the VolRelTracker
|
|
io.inner.release.ready := irel_can_merge || irel_same_xact
|
|
|
|
mergeDataInner(io.inner.release)
|
|
|
|
// If there was a writeback, forward it outwards
|
|
outerRelease(
|
|
coh = outer_coh.onHit(M_XWR),
|
|
data = data_buffer(vol_ognt_counter.up.idx))
|
|
|
|
// Send outer request for miss
|
|
outerAcquire(
|
|
caching = !xact_iacq.isBuiltInType(),
|
|
coh = outer_coh,
|
|
data = data_buffer(ognt_counter.up.idx),
|
|
wmask = wmask_buffer(ognt_counter.up.idx),
|
|
next = s_busy)
|
|
|
|
// Handle the response from outer memory
|
|
mergeDataOuter(io.outer.grant)
|
|
|
|
// Acknowledge or respond with data
|
|
innerGrant(
|
|
data = data_buffer(ignt_data_idx),
|
|
external_pending = pending_orel || ognt_counter.pending || vol_ognt_counter.pending)
|
|
|
|
when(iacq_is_allocating) {
|
|
initializeProbes()
|
|
}
|
|
|
|
initDataInner(io.inner.acquire, iacq_is_allocating || iacq_is_merging)
|
|
|
|
// Wait for everything to quiesce
|
|
quiesce() { clearWmaskBuffer() }
|
|
}
|