From 11b5222d014a425ac65545491e92800b69c01336 Mon Sep 17 00:00:00 2001 From: Henry Cook Date: Tue, 21 Apr 2015 22:23:04 -0700 Subject: [PATCH] Refactored WritebackUnit --- uncore/src/main/scala/cache.scala | 245 +++++++++++++----------------- 1 file changed, 105 insertions(+), 140 deletions(-) diff --git a/uncore/src/main/scala/cache.scala b/uncore/src/main/scala/cache.scala index 4adb09a3..32529202 100644 --- a/uncore/src/main/scala/cache.scala +++ b/uncore/src/main/scala/cache.scala @@ -192,9 +192,8 @@ abstract class L2HellaCacheModule extends Module with L2HellaCacheParameters { } def doInternalInputRouting[T <: HasL2Id](in: ValidIO[T], outs: Seq[ValidIO[T]]) { - val idx = in.bits.id outs.map(_.bits := in.bits) - outs.zipWithIndex.map { case (o,i) => o.valid := in.valid && idx === UInt(i) } + outs.zipWithIndex.map { case (o,i) => o.valid := in.valid && in.bits.id === UInt(i) } } } @@ -621,7 +620,7 @@ class L2AcquireTracker(trackerId: Int) extends L2XactTracker { max = io.inner.tlNCachingClients, up = io.inner.probe, down = io.inner.release)._1 - val (pending_ognts, oacq_data_idx, oacq_data_done, ognt_data_idx, ognt_data_done) = + val (pending_ognt, oacq_data_idx, oacq_data_done, ognt_data_idx, ognt_data_done) = connectTwoWayBeatCounter( max = 1, up = io.outer.acquire, @@ -646,7 +645,7 @@ class L2AcquireTracker(trackerId: Int) extends L2XactTracker { pending_writes.orR || pending_resps.orR || pending_puts.orR || - pending_ognts || + pending_ognt || ignt_q.io.count > UInt(0) || //pending_meta_write || // Has own state: s_meta_write pending_ifins) @@ -794,8 +793,7 @@ class L2AcquireTracker(trackerId: Int) extends L2XactTracker { ignt_q.io.deq.ready := ignt_data_done io.inner.grant.valid := state === s_busy && ignt_q.io.deq.valid && - (!io.ignt().hasData() || - pending_ignt_data(ignt_data_idx)) + (!io.ignt().hasData() || pending_ignt_data(ignt_data_idx)) // Make the Grant message using the data stored in the secondary miss queue io.inner.grant.bits := pending_coh.inner.makeGrant( pri = xact, @@ -826,9 +824,7 @@ class L2AcquireTracker(trackerId: Int) extends L2XactTracker { addPendingBitWhenBeatIsGetOrAtomic(io.inner.acquire) | addPendingBitWhenBeatHasPartialWritemask(io.inner.acquire) val curr_read_beat = PriorityEncoder(pending_reads) - io.data.read.valid := state === s_busy && - pending_reads.orR && - !pending_ognts + io.data.read.valid := state === s_busy && pending_reads.orR && !pending_ognt io.data.read.bits.id := UInt(trackerId) io.data.read.bits.way_en := xact_way_en io.data.read.bits.addr_idx := xact.addr_block(idxMSB,idxLSB) @@ -847,7 +843,7 @@ class L2AcquireTracker(trackerId: Int) extends L2XactTracker { val curr_write_beat = PriorityEncoder(pending_writes) io.data.write.valid := state === s_busy && pending_writes.orR && - !pending_ognts && + !pending_ognt && !pending_reads(curr_write_beat) && !pending_resps(curr_write_beat) io.data.write.bits.id := UInt(trackerId) @@ -870,10 +866,11 @@ class L2AcquireTracker(trackerId: Int) extends L2XactTracker { io.meta.write.bits.data.coh := pending_coh io.wb.req.valid := state === s_wb_req - io.wb.req.bits.addr_block := Cat(xact_old_meta.tag, xact.addr_block(idxMSB,idxLSB)) + io.wb.req.bits.id := UInt(trackerId) + io.wb.req.bits.idx := xact.addr_block(idxMSB,idxLSB) + io.wb.req.bits.tag := xact_old_meta.tag io.wb.req.bits.coh := xact_old_meta.coh io.wb.req.bits.way_en := xact_way_en - io.wb.req.bits.id := UInt(trackerId) // Handling of secondary misses (Gets and Puts only for now) when(io.inner.acquire.fire() && io.iacq().hasData()) { // state <= s_meta_wrtie @@ -961,9 +958,7 @@ class L2AcquireTracker(trackerId: Int) extends L2XactTracker { // These IOs are used for routing in the parent val in_same_set = xact.addr_block(idxMSB,idxLSB) === io.iacq().addr_block(idxMSB,idxLSB) - io.has_release_match := xact.conflicts(io.irel()) && - !io.irel().isVoluntary() && - (state === s_inner_probe) + io.has_release_match := xact.conflicts(io.irel()) && !io.irel().isVoluntary() && io.inner.release.ready io.has_acquire_match := can_merge_iacq_put || can_merge_iacq_get io.has_acquire_conflict := in_same_set && (state != s_idle) && !io.has_acquire_match //TODO: relax from in_same_set to xact.conflicts(io.iacq())? @@ -974,10 +969,8 @@ class L2AcquireTracker(trackerId: Int) extends L2XactTracker { "AcquireTracker accepted data beat from different network source than initial request.") } -class L2WritebackReq extends L2HellaCacheBundle - with HasL2Id { - val addr_block = UInt(width = blockAddrBits) // TODO: assumes same block size - val coh = new HierarchicalMetadata +class L2WritebackReq extends L2Metadata with HasL2Id { + val idx = Bits(width = idxBits) val way_en = Bits(width = nWays) } @@ -996,136 +989,108 @@ class L2WritebackUnitIO extends HierarchicalXactTrackerIO { class L2WritebackUnit(trackerId: Int) extends L2XactTracker { val io = new L2WritebackUnitIO - val s_idle :: s_inner_probe :: s_data_read :: s_data_resp :: s_outer_release :: s_outer_grant :: s_wb_resp :: Nil = Enum(UInt(), 7) + val s_idle :: s_inner_probe :: s_busy :: s_outer_grant :: s_wb_resp :: Nil = Enum(UInt(), 5) val state = Reg(init=s_idle) - val xact_addr_block = Reg(io.wb.req.bits.addr_block.clone) - val xact_coh = Reg{ new HierarchicalMetadata } - val xact_way_en = Reg{ Bits(width = nWays) } - val data_buffer = Vec.fill(innerDataBeats){ Reg(io.irel().data.clone) } - val xact_id = Reg{ UInt() } + val xact = Reg(new L2WritebackReq) + val data_buffer = Vec.fill(innerDataBeats){ Reg(init=UInt(0, width = innerDataBits)) } + val xact_addr_block = Cat(xact.tag, xact.idx) - val irel_had_data = Reg(init = Bool(false)) - val irel_cnt = Reg(init = UInt(0, width = log2Up(io.inner.tlNCachingClients+1))) - val pending_probes = Reg(init = Bits(0, width = io.inner.tlNCachingClients)) - val curr_probe_dst = PriorityEncoder(pending_probes) - val full_sharers = io.wb.req.bits.coh.inner.full() - val mask_incoherent = full_sharers & ~io.incoherent.toBits + val pending_irels = + connectTwoWayBeatCounter(max = io.inner.tlNCachingClients, up = io.inner.probe, down = io.inner.release)._1 + val (pending_ognt, orel_data_idx, orel_data_done, ognt_data_idx, ognt_data_done) = + connectTwoWayBeatCounter(max = 1, up = io.outer.release, down = io.outer.grant) + val pending_iprbs = Reg(init = Bits(0, width = io.inner.tlNCachingClients)) + val pending_reads = Reg(init=Bits(0, width = io.inner.tlDataBeats)) + val pending_resps = Reg(init=Bits(0, width = io.inner.tlDataBeats)) + val pending_orel_data = Reg(init=Bits(0, width = io.inner.tlDataBeats)) - val irel_data_done = connectIncomingDataBeatCounter(io.inner.release) - val (orel_data_cnt, orel_data_done) = connectOutgoingDataBeatCounter(io.outer.release) - val (read_data_cnt, read_data_done) = connectInternalDataBeatCounter(io.data.read) - val resp_data_done = connectInternalDataBeatCounter(io.data.resp) + // Start the writeback sub-transaction + io.wb.req.ready := state === s_idle - val pending_icoh_on_irel = xact_coh.inner.onRelease(io.irel()) - val pending_ocoh_on_irel = xact_coh.outer.onHit(M_XWR) // WB is a write - val pending_ofin_on_ognt = io.ognt().makeFinish() + // Track which clients yet need to be probed and make Probe message + pending_iprbs := pending_iprbs & dropPendingBitAtDest(io.inner.probe) + val curr_probe_dst = PriorityEncoder(pending_iprbs) + io.inner.probe.valid := state === s_inner_probe && pending_iprbs.orR + io.inner.probe.bits := xact.coh.inner.makeProbeForVoluntaryWriteback(curr_probe_dst, xact_addr_block) - io.has_acquire_conflict := Bool(false) - io.has_acquire_match := Bool(false) - io.has_release_match := !io.irel().isVoluntary() && - io.irel().conflicts(xact_addr_block) && - (state === s_inner_probe) + // Handle incoming releases from clients, which may reduce sharer counts + // and/or write back dirty data + val inner_coh_on_irel = xact.coh.inner.onRelease(io.irel()) + val outer_coh_on_irel = xact.coh.outer.onHit(M_XWR) + io.inner.release.ready := state === s_inner_probe || state === s_busy + when(io.inner.release.fire()) { + xact.coh.inner := inner_coh_on_irel + when(io.irel().hasData()) { xact.coh.outer := outer_coh_on_irel } // WB is a write + data_buffer(io.inner.release.bits.addr_beat) := io.inner.release.bits.data + } - io.outer.acquire.valid := Bool(false) - io.outer.probe.ready := Bool(false) - io.outer.release.valid := Bool(false) // default - io.outer.release.bits := xact_coh.outer.makeVoluntaryWriteback( - client_xact_id = UInt(trackerId), - addr_block = xact_addr_block, - addr_beat = orel_data_cnt, - data = data_buffer(orel_data_cnt)) - io.outer.grant.ready := Bool(false) // default - - io.inner.probe.valid := Bool(false) - io.inner.probe.bits := xact_coh.inner.makeProbeForVoluntaryWriteback(curr_probe_dst, xact_addr_block) - - io.inner.grant.valid := Bool(false) - io.inner.acquire.ready := Bool(false) - io.inner.release.ready := Bool(false) - io.inner.finish.ready := Bool(false) - - io.data.read.valid := Bool(false) + // If a release didn't write back data, have to read it from data array + pending_reads := (pending_reads & + dropPendingBit(io.data.read) & + dropPendingBitWhenBeatHasData(io.inner.release)) + val curr_read_beat = PriorityEncoder(pending_reads) + io.data.read.valid := state === s_busy && pending_reads.orR io.data.read.bits.id := UInt(trackerId) - io.data.read.bits.way_en := xact_way_en - io.data.read.bits.addr_idx := xact_addr_block(idxMSB,idxLSB) - io.data.read.bits.addr_beat := read_data_cnt + io.data.read.bits.way_en := xact.way_en + io.data.read.bits.addr_idx := xact.idx + io.data.read.bits.addr_beat := curr_read_beat io.data.write.valid := Bool(false) - io.wb.req.ready := Bool(false) - io.wb.resp.valid := Bool(false) - io.wb.resp.bits.id := xact_id - - switch (state) { - is(s_idle) { - io.wb.req.ready := Bool(true) - when(io.wb.req.valid) { - xact_addr_block := io.wb.req.bits.addr_block - xact_coh := io.wb.req.bits.coh - xact_way_en := io.wb.req.bits.way_en - xact_id := io.wb.req.bits.id - irel_had_data := Bool(false) - val needs_inner_probes = io.wb.req.bits.coh.inner.requiresProbesOnVoluntaryWriteback() - when(needs_inner_probes) { - pending_probes := mask_incoherent - irel_cnt := PopCount(mask_incoherent) - } - state := Mux(needs_inner_probes, s_inner_probe, s_data_read) - } - } - is(s_inner_probe) { - // Send probes - io.inner.probe.valid := pending_probes != UInt(0) - when(io.inner.probe.ready) { - pending_probes := pending_probes & ~UIntToOH(curr_probe_dst) - } - // Handle releases, which may have data being written back - io.inner.release.ready := Bool(true) - when(io.inner.release.valid) { - xact_coh.inner := pending_icoh_on_irel - // Handle released dirty data - when(io.irel().hasData()) { - irel_had_data := Bool(true) - xact_coh.outer := pending_ocoh_on_irel - data_buffer(io.irel().addr_beat) := io.irel().data - } - // We don't decrement irel_cnt until we've received all the data beats. - when(!io.irel().hasData() || irel_data_done) { - irel_cnt := irel_cnt - UInt(1) - } - } - when(irel_cnt === UInt(0)) { - state := Mux(irel_had_data, // If someone released a dirty block - s_outer_release, // write that block back, otherwise - Mux(xact_coh.outer.requiresVoluntaryWriteback(), - s_data_read, // write extant dirty data back, or just - s_wb_resp)) // drop a clean block after collecting acks - } - } - is(s_data_read) { - io.data.read.valid := Bool(true) - when(io.data.resp.valid) { data_buffer(io.data.resp.bits.addr_beat) := io.data.resp.bits.data } - when(read_data_done) { state := s_data_resp } - } - is(s_data_resp) { - when(io.data.resp.valid) { data_buffer(io.data.resp.bits.addr_beat) := io.data.resp.bits.data } - when(resp_data_done) { state := s_outer_release } - } - is(s_outer_release) { - io.outer.release.valid := Bool(true) - when(orel_data_done) { - state := Mux(io.orel().requiresAck(), s_outer_grant, s_wb_resp) - } - } - is(s_outer_grant) { - io.outer.grant.ready := Bool(true) - when(io.outer.grant.valid) { - state := s_wb_resp - } - } - is(s_wb_resp) { - io.wb.resp.valid := Bool(true) - state := s_idle - } + pending_resps := (pending_resps & dropPendingBitInternal(io.data.resp)) | + addPendingBitInternal(io.data.read) + when(io.data.resp.valid) { + data_buffer(io.data.resp.bits.addr_beat) := io.data.resp.bits.data } + + // Once the data is buffered we can write it back to outer memory + pending_orel_data := pending_orel_data | + addPendingBitWhenBeatHasData(io.inner.release) | + addPendingBitInternal(io.data.resp) + io.outer.release.valid := state === s_busy && + (!io.orel().hasData() || pending_orel_data(orel_data_idx)) + io.outer.release.bits := xact.coh.outer.makeVoluntaryWriteback( + client_xact_id = UInt(trackerId), + addr_block = xact_addr_block, + addr_beat = orel_data_idx, + data = data_buffer(orel_data_idx)) + + // Wait for an acknowledgement + io.outer.grant.ready := state === s_outer_grant + + // Respond to the initiating transaction handler signalling completion of the writeback + io.wb.resp.valid := state === s_wb_resp + io.wb.resp.bits.id := xact.id + + // State machine updates and transaction handler metadata intialization + when(state === s_idle && io.wb.req.valid) { + xact := io.wb.req.bits + val coh = io.wb.req.bits.coh + val needs_inner_probes = coh.inner.requiresProbesOnVoluntaryWriteback() + when(needs_inner_probes) { pending_iprbs := coh.inner.full() & ~io.incoherent.toBits } + pending_reads := SInt(-1, width = innerDataBeats) + pending_resps := UInt(0) + pending_orel_data := UInt(0) + state := Mux(needs_inner_probes, s_inner_probe, s_busy) + } + when(state === s_inner_probe && !(pending_iprbs.orR || pending_irels)) { + state := Mux(xact.coh.outer.requiresVoluntaryWriteback(), s_busy, s_wb_resp) + } + when(state === s_busy && orel_data_done) { + state := Mux(io.orel().requiresAck(), s_outer_grant, s_wb_resp) + } + when(state === s_outer_grant && ognt_data_done) { state := s_wb_resp } + when(state === s_wb_resp ) { state := s_idle } + + // These IOs are used for routing in the parent + io.has_release_match := io.irel().conflicts(xact_addr_block) && !io.irel().isVoluntary() && io.inner.release.ready + io.has_acquire_match := Bool(false) + io.has_acquire_conflict := Bool(false) + + // IOs pinned low + io.outer.acquire.valid := Bool(false) + io.outer.probe.ready := Bool(false) // TODO: handle these here + io.inner.grant.valid := Bool(false) + io.inner.acquire.ready := Bool(false) + io.inner.finish.ready := Bool(false) }