xref: /XiangShan/src/main/scala/xiangshan/cache/dcache/mainpipe/WritebackQueue.scala (revision 5c01cc3cf38f617ae6c9d841171531cceabb12bd)
1/***************************************************************************************
2* Copyright (c) 2020-2021 Institute of Computing Technology, Chinese Academy of Sciences
3* Copyright (c) 2020-2021 Peng Cheng Laboratory
4*
5* XiangShan is licensed under Mulan PSL v2.
6* You can use this software according to the terms and conditions of the Mulan PSL v2.
7* You may obtain a copy of Mulan PSL v2 at:
8*          http://license.coscl.org.cn/MulanPSL2
9*
10* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
11* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
12* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
13*
14* See the Mulan PSL v2 for more details.
15***************************************************************************************/
16
17package xiangshan.cache
18
19import chipsalliance.rocketchip.config.Parameters
20import chisel3._
21import chisel3.util._
22import freechips.rocketchip.tilelink.TLPermissions._
23import freechips.rocketchip.tilelink.{TLArbiter, TLBundleC, TLBundleD, TLEdgeOut}
24import huancun.DirtyKey
25import utils.{HasPerfEvents, HasTLDump, XSDebug, XSPerfAccumulate}
26
27class WritebackReqWodata(implicit p: Parameters) extends DCacheBundle {
28  val addr = UInt(PAddrBits.W)
29  val addr_dup_0 = UInt(PAddrBits.W)
30  val addr_dup_1 = UInt(PAddrBits.W)
31  val param  = UInt(cWidth.W)
32  val voluntary = Bool()
33  val hasData = Bool()
34  val dirty = Bool()
35
36  val delay_release = Bool()
37  val miss_id = UInt(log2Up(cfg.nMissEntries).W)
38
39  def dump() = {
40    XSDebug("WritebackReq addr: %x param: %d voluntary: %b hasData: %b\n",
41      addr, param, voluntary, hasData)
42  }
43}
44
45class WritebackReqData(implicit p: Parameters) extends DCacheBundle {
46  val data = UInt((cfg.blockBytes * 8).W)
47}
48
49class WritebackReq(implicit p: Parameters) extends WritebackReqWodata {
50  val data = UInt((cfg.blockBytes * 8).W)
51
52  override def dump() = {
53    XSDebug("WritebackReq addr: %x param: %d voluntary: %b hasData: %b data: %x\n",
54      addr, param, voluntary, hasData, data)
55  }
56
57  def toWritebackReqWodata(): WritebackReqWodata = {
58    val out = Wire(new WritebackReqWodata)
59    out.addr := addr
60    out.addr_dup_0 := addr_dup_0
61    out.addr_dup_1 := addr_dup_1
62    out.param := param
63    out.voluntary := voluntary
64    out.hasData := hasData
65    out.dirty := dirty
66    out.delay_release := delay_release
67    out.miss_id := miss_id
68    out
69  }
70
71  def toWritebackReqData(): WritebackReqData = {
72    val out = Wire(new WritebackReqData)
73    out.data := data
74    out
75  }
76}
77
78// While a Release sleeps and waits for a refill to wake it up,
79// main pipe might update meta & data during this time.
80// So the meta & data to be released need to be updated too.
81class ReleaseUpdate(implicit p: Parameters) extends DCacheBundle {
82  // only consider store here
83  val addr = UInt(PAddrBits.W)
84  val mask = UInt(DCacheBanks.W)
85  val data = UInt((cfg.blockBytes * 8).W)
86}
87
88// To reduce fanout, miss queue entry data is updated 1 cycle
89// after ReleaseUpdate.fire()
90class MissQueueEntryReleaseUpdate(implicit p: Parameters) extends DCacheBundle {
91  // only consider store here
92  val addr = UInt(PAddrBits.W)
93  val mask_delayed = UInt(DCacheBanks.W)
94  val data_delayed = UInt((cfg.blockBytes * 8).W)
95  val mask_orr = Bool()
96}
97
98class WritebackEntry(edge: TLEdgeOut)(implicit p: Parameters) extends DCacheModule with HasTLDump
99{
100  val io = IO(new Bundle {
101    val id = Input(UInt())
102    // allocate this entry for new req
103    val primary_valid = Input(Bool())
104    // this entry is free and can be allocated to new reqs
105    val primary_ready = Output(Bool())
106    val primary_ready_dup = Vec(4, Output(Bool()))
107    // this entry is busy, but it can merge the new req
108    val secondary_valid = Input(Bool())
109    val secondary_ready = Output(Bool())
110    val req = Flipped(DecoupledIO(new WritebackReqWodata))
111    val req_data = Input(new WritebackReqData)
112
113    val mem_release = DecoupledIO(new TLBundleC(edge.bundle))
114    val mem_grant = Flipped(DecoupledIO(new TLBundleD(edge.bundle)))
115
116    val block_addr  = Output(Valid(UInt()))
117
118    val release_wakeup = Flipped(ValidIO(UInt(log2Up(cfg.nMissEntries).W)))
119    val release_update = Flipped(ValidIO(new MissQueueEntryReleaseUpdate))
120  })
121
122  val s_invalid :: s_sleep :: s_release_req :: s_release_resp :: Nil = Enum(4)
123  // ProbeAck:               s_invalid ->            s_release_req
124  // ProbeAck merge Release: s_invalid ->            s_release_req
125  // Release:                s_invalid -> s_sleep -> s_release_req -> s_release_resp
126  // Release merge ProbeAck: s_invalid -> s_sleep -> s_release_req
127  //                        (change Release into ProbeAck when Release is not fired)
128  //                     or: s_invalid -> s_sleep -> s_release_req -> s_release_resp -> s_release_req
129  //                        (send a ProbeAck after Release transaction is over)
130  val state = RegInit(s_invalid)
131  val state_dup_0 = RegInit(s_invalid)
132  val state_dup_1 = RegInit(s_invalid)
133  val state_dup_for_mp = RegInit(VecInit(Seq.fill(4)(s_invalid)))
134
135  // internal regs
136  // remaining beats
137  val remain = RegInit(0.U(refillCycles.W))
138  val remain_dup_0 = RegInit(0.U(refillCycles.W))
139  val remain_dup_1 = RegInit(0.U(refillCycles.W))
140  val remain_set = WireInit(0.U(refillCycles.W))
141  val remain_clr = WireInit(0.U(refillCycles.W))
142  remain := (remain | remain_set) & ~remain_clr
143  remain_dup_0 := (remain_dup_0 | remain_set) & ~remain_clr
144  remain_dup_1 := (remain_dup_1 | remain_set) & ~remain_clr
145
146  // writeback queue data
147  val data = Reg(UInt((cfg.blockBytes * 8).W))
148
149  // pending data write
150  // !s_data_override means there is an in-progress data write
151  val s_data_override = RegInit(true.B)
152  // !s_data_merge means there is an in-progress data merge
153  val s_data_merge = RegInit(true.B)
154
155  // there are valid request that can be sent to release bus
156  val busy = remain.orR && s_data_override && s_data_merge // have remain beats and data write finished
157
158  val req  = Reg(new WritebackReqWodata)
159
160  // assign default signals to output signals
161  io.req.ready := false.B
162  io.mem_release.valid := false.B
163  io.mem_release.bits  := DontCare
164  io.mem_grant.ready   := false.B
165  io.block_addr.valid  := state =/= s_invalid
166  io.block_addr.bits   := req.addr
167
168  s_data_override := true.B // data_override takes only 1 cycle
169  s_data_merge := true.B // data_merge takes only 1 cycle
170
171
172  when (state =/= s_invalid) {
173    XSDebug("WritebackEntry: %d state: %d block_addr: %x\n", io.id, state, io.block_addr.bits)
174  }
175
176  def mergeData(old_data: UInt, new_data: UInt, wmask: UInt): UInt = {
177    val full_wmask = FillInterleaved(64, wmask)
178    (~full_wmask & old_data | full_wmask & new_data)
179  }
180
181  // --------------------------------------------------------------------------------
182  // s_invalid: receive requests
183  // new req entering
184  when (io.req.valid && io.primary_valid && io.primary_ready) {
185    assert (remain === 0.U)
186    req := io.req.bits
187    s_data_override := false.B
188    when (io.req.bits.delay_release) {
189      state := s_sleep
190      state_dup_0 := s_sleep
191      state_dup_1 := s_sleep
192      state_dup_for_mp.foreach(_ := s_sleep)
193    }.otherwise {
194      state := s_release_req
195      state_dup_0 := s_release_req
196      state_dup_1 := s_release_req
197      state_dup_for_mp.foreach(_ := s_release_req)
198      remain_set := Mux(io.req.bits.hasData, ~0.U(refillCycles.W), 1.U(refillCycles.W))
199    }
200  }
201
202  // --------------------------------------------------------------------------------
203  // s_sleep: wait for refill pipe to inform me that I can keep releasing
204  val merge = io.secondary_valid && io.secondary_ready
205  when (state === s_sleep) {
206    assert(remain === 0.U)
207    // There shouldn't be a new Release with the same addr in sleep state
208    assert(!(merge && io.req.bits.voluntary))
209
210    val update = io.release_update.valid && io.release_update.bits.addr === req.addr
211    when (update) {
212      req.hasData := req.hasData || io.release_update.bits.mask_orr
213      req.dirty := req.dirty || io.release_update.bits.mask_orr
214      s_data_merge := false.B
215    }.elsewhen (merge) {
216      state := s_release_req
217      state_dup_0 := s_release_req
218      state_dup_1 := s_release_req
219      state_dup_for_mp.foreach(_ := s_release_req)
220      req.voluntary := false.B
221      req.param := req.param
222      req.hasData := req.hasData || io.req.bits.hasData
223      req.dirty := req.dirty || io.req.bits.dirty
224      s_data_override := !io.req.bits.hasData // update data when io.req.bits.hasData
225      req.delay_release := false.B
226      remain_set := Mux(req.hasData || io.req.bits.hasData, ~0.U(refillCycles.W), 1.U(refillCycles.W))
227    }
228
229    when (io.release_wakeup.valid && io.release_wakeup.bits === req.miss_id) {
230      state := s_release_req
231      state_dup_0 := s_release_req
232      state_dup_1 := s_release_req
233      state_dup_for_mp.foreach(_ := s_release_req)
234      req.delay_release := false.B
235      remain_set := Mux(
236        req.hasData || update && io.release_update.bits.mask_orr || merge && io.req.bits.hasData,
237        ~0.U(refillCycles.W),
238        1.U(refillCycles.W)
239      )
240    }
241  }
242
243  // --------------------------------------------------------------------------------
244  // while there beats remaining to be sent, we keep sending
245  // which beat to send in this cycle?
246  val beat = PriorityEncoder(remain_dup_0)
247
248  val beat_data = Wire(Vec(refillCycles, UInt(beatBits.W)))
249  for (i <- 0 until refillCycles) {
250    beat_data(i) := data((i + 1) * beatBits - 1, i * beatBits)
251  }
252
253  val probeResponse = edge.ProbeAck(
254    fromSource = io.id,
255    toAddress = req.addr_dup_0,
256    lgSize = log2Ceil(cfg.blockBytes).U,
257    reportPermissions = req.param
258  )
259
260  val probeResponseData = edge.ProbeAck(
261    fromSource = io.id,
262    toAddress = req.addr_dup_0,
263    lgSize = log2Ceil(cfg.blockBytes).U,
264    reportPermissions = req.param,
265    data = beat_data(beat)
266  )
267
268  val voluntaryRelease = edge.Release(
269    fromSource = io.id,
270    toAddress = req.addr_dup_1,
271    lgSize = log2Ceil(cfg.blockBytes).U,
272    shrinkPermissions = req.param
273  )._2
274
275  val voluntaryReleaseData = edge.Release(
276    fromSource = io.id,
277    toAddress = req.addr_dup_1,
278    lgSize = log2Ceil(cfg.blockBytes).U,
279    shrinkPermissions = req.param,
280    data = beat_data(beat)
281  )._2
282
283  voluntaryReleaseData.echo.lift(DirtyKey).foreach(_ := req.dirty)
284  when(busy) {
285    assert(!req.dirty || req.hasData)
286  }
287
288  io.mem_release.valid := busy
289  io.mem_release.bits  := Mux(req.voluntary,
290    Mux(req.hasData, voluntaryReleaseData, voluntaryRelease),
291    Mux(req.hasData, probeResponseData, probeResponse))
292
293  when (io.mem_release.fire()) { remain_clr := PriorityEncoderOH(remain_dup_1) }
294
295  val (_, _, release_done, _) = edge.count(io.mem_release)
296
297//  when (state === s_release_req && release_done) {
298//    state := Mux(req.voluntary, s_release_resp, s_invalid)
299//  }
300
301  // Because now wbq merges a same-addr req unconditionally, when the req to be merged comes too late,
302  // the previous req might not be able to merge. Thus we have to handle the new req later after the
303  // previous one finishes.
304  // TODO: initiate these
305  val release_later = RegInit(false.B)
306  val c_already_sent = RegInit(false.B)
307  def tmp_req() = new Bundle {
308    val param = UInt(cWidth.W)
309    val voluntary = Bool()
310    val hasData = Bool()
311    val dirty = Bool()
312    val delay_release = Bool()
313    val miss_id = UInt(log2Up(cfg.nMissEntries).W)
314
315    def toWritebackReq = {
316      val r = Wire(new WritebackReq())
317      r.data := data
318      r.addr := req.addr
319      r.addr_dup_0 := req.addr_dup_0
320      r.addr_dup_1 := req.addr_dup_1
321      r.param := param
322      r.voluntary := voluntary
323      r.hasData := hasData
324      r.dirty := dirty
325      r.delay_release := delay_release
326      r.miss_id := miss_id
327      r
328    }
329  }
330  val req_later = Reg(tmp_req())
331
332  when (state_dup_0 === s_release_req) {
333    when (io.mem_release.fire()) {
334      c_already_sent := !release_done
335    }
336
337    when (req.voluntary) {
338      // The previous req is Release
339      when (release_done) {
340        state := s_release_resp
341        state_dup_0 := s_release_resp
342        state_dup_1 := s_release_resp
343        state_dup_for_mp.foreach(_ := s_release_resp)
344      }
345      // merge a ProbeAck
346      when (merge) {
347        when (io.mem_release.fire() || c_already_sent) {
348          // too late to merge, handle the ProbeAck later
349          release_later := true.B
350          req_later.param := io.req.bits.param
351          req_later.voluntary := io.req.bits.voluntary
352          req_later.hasData := io.req.bits.hasData
353          req_later.dirty := io.req.bits.dirty
354          req_later.delay_release := io.req.bits.delay_release
355          req_later.miss_id := io.req.bits.miss_id
356        }.otherwise {
357          // Release hasn't been sent out yet, change Release to ProbeAck
358          req.voluntary := false.B
359          req.hasData := req.hasData || io.req.bits.hasData
360          req.dirty := req.dirty || io.req.bits.dirty
361          // s_data_override := false.B
362          req.delay_release := false.B
363          remain_set := Mux(req.hasData || io.req.bits.hasData, ~0.U(refillCycles.W), 1.U(refillCycles.W))
364        }
365      }
366    }.otherwise {
367      // The previous req is ProbeAck
368      when (merge) {
369        release_later := true.B
370        req_later.param := io.req.bits.param
371        req_later.voluntary := io.req.bits.voluntary
372        req_later.hasData := io.req.bits.hasData
373        req_later.dirty := io.req.bits.dirty
374        req_later.delay_release := io.req.bits.delay_release
375        req_later.miss_id := io.req.bits.miss_id
376      }
377
378      when (release_done) {
379        when (merge) {
380          // Send the Release after ProbeAck
381//          state := s_release_req
382//          req := Mux(merge, io.req.bits, req_later.toWritebackReq)
383//          release_later := false.B
384          state := s_sleep
385          state_dup_0 := s_sleep
386          state_dup_1 := s_sleep
387          state_dup_for_mp.foreach(_ := s_sleep)
388          req := io.req.bits
389          release_later := false.B
390        }.elsewhen (release_later) {
391          state := Mux(
392            io.release_wakeup.valid && io.release_wakeup.bits === req_later.miss_id || !req_later.delay_release,
393            s_release_req,
394            s_sleep
395          )
396          state_dup_0 := Mux(
397            io.release_wakeup.valid && io.release_wakeup.bits === req_later.miss_id || !req_later.delay_release,
398            s_release_req,
399            s_sleep
400          )
401          state_dup_1 := Mux(
402            io.release_wakeup.valid && io.release_wakeup.bits === req_later.miss_id || !req_later.delay_release,
403            s_release_req,
404            s_sleep
405          )
406          state_dup_for_mp.foreach(_ := Mux(
407            io.release_wakeup.valid && io.release_wakeup.bits === req_later.miss_id || !req_later.delay_release,
408            s_release_req,
409            s_sleep
410          ))
411          req := req_later.toWritebackReq
412          when (io.release_wakeup.valid && io.release_wakeup.bits === req_later.miss_id) {
413            req.delay_release := false.B
414          }
415          release_later := false.B
416        }.otherwise {
417          state := s_invalid
418          state_dup_0 := s_invalid
419          state_dup_1 := s_invalid
420          state_dup_for_mp.foreach(_ := s_invalid)
421          release_later := false.B
422        }
423      }
424
425      when (io.release_wakeup.valid && io.release_wakeup.bits === req_later.miss_id) {
426        req_later.delay_release := false.B
427      }
428    }
429  }
430
431  // --------------------------------------------------------------------------------
432  // receive ReleaseAck for Releases
433  when (state_dup_0 === s_release_resp) {
434    io.mem_grant.ready := true.B
435
436    when (merge) {
437      release_later := true.B
438      req_later.param := io.req.bits.param
439      req_later.voluntary := io.req.bits.voluntary
440      req_later.hasData := io.req.bits.hasData
441      req_later.dirty := io.req.bits.dirty
442      req_later.delay_release := io.req.bits.delay_release
443      req_later.miss_id := io.req.bits.miss_id
444    }
445    when (io.mem_grant.fire()) {
446      when (merge) {
447        state := s_release_req
448        state_dup_0 := s_release_req
449        state_dup_1 := s_release_req
450        state_dup_for_mp.foreach(_ := s_release_req)
451        req := io.req.bits
452        remain_set := Mux(io.req.bits.hasData, ~0.U(refillCycles.W), 1.U(refillCycles.W))
453        release_later := false.B
454      }.elsewhen(release_later) {
455        state := s_release_req
456        state_dup_0 := s_release_req
457        state_dup_1 := s_release_req
458        state_dup_for_mp.foreach(_ := s_release_req)
459        req := req_later.toWritebackReq
460        remain_set := Mux(req_later.hasData, ~0.U(refillCycles.W), 1.U(refillCycles.W))
461        release_later := false.B
462      }.otherwise {
463        state := s_invalid
464        state_dup_0 := s_invalid
465        state_dup_1 := s_invalid
466        state_dup_for_mp.foreach(_ := s_invalid)
467        release_later := false.B
468      }
469    }
470  }
471
472  // When does this entry merge a new req?
473  // 1. When this entry is free
474  // 2. When this entry wants to release while still waiting for release_wakeup signal,
475  //    and a probe req with the same addr comes. In this case we merge probe with release,
476  //    handle this probe, so we don't need another release.
477  io.primary_ready := state_dup_1 === s_invalid
478  io.primary_ready_dup.zip(state_dup_for_mp).foreach { case (rdy, st) => rdy := st === s_invalid }
479  io.secondary_ready := state_dup_1 =/= s_invalid && io.req.bits.addr === req.addr
480
481  // data update logic
482  when (!s_data_merge) {
483    data := mergeData(data, io.release_update.bits.data_delayed, io.release_update.bits.mask_delayed)
484  }
485
486  when (!s_data_override && req.hasData) {
487    data := io.req_data.data
488  }
489
490  assert(!RegNext(!s_data_merge && !s_data_override))
491
492  // performance counters
493  XSPerfAccumulate("wb_req", io.req.fire())
494  XSPerfAccumulate("wb_release", state === s_release_req && release_done && req.voluntary)
495  XSPerfAccumulate("wb_probe_resp", state_dup_0 === s_release_req && release_done && !req.voluntary)
496  XSPerfAccumulate("penalty_blocked_by_channel_C", io.mem_release.valid && !io.mem_release.ready)
497  XSPerfAccumulate("penalty_waiting_for_channel_D", io.mem_grant.ready && !io.mem_grant.valid && state_dup_1 === s_release_resp)
498}
499
500class WritebackQueue(edge: TLEdgeOut)(implicit p: Parameters) extends DCacheModule with HasTLDump with HasPerfEvents {
501  val io = IO(new Bundle {
502    val req = Flipped(DecoupledIO(new WritebackReq))
503    val req_ready_dup = Vec(4, Output(Bool()))
504    val mem_release = DecoupledIO(new TLBundleC(edge.bundle))
505    val mem_grant = Flipped(DecoupledIO(new TLBundleD(edge.bundle)))
506
507    val release_wakeup = Flipped(ValidIO(UInt(log2Up(cfg.nMissEntries).W)))
508    val release_update = Flipped(ValidIO(new ReleaseUpdate))
509
510    val miss_req = Flipped(Valid(UInt()))
511    val block_miss_req = Output(Bool())
512  })
513
514  require(cfg.nReleaseEntries > cfg.nMissEntries)
515
516  val primary_ready_vec = Wire(Vec(cfg.nReleaseEntries, Bool()))
517  val secondary_ready_vec = Wire(Vec(cfg.nReleaseEntries, Bool()))
518  val accept = Cat(primary_ready_vec).orR
519  val merge = Cat(secondary_ready_vec).orR
520  val alloc = accept && !merge
521  // When there are empty entries, merge or allocate a new entry.
522  // When there is no empty entry, reject it even if it can be merged.
523  io.req.ready := accept
524
525  // assign default values to output signals
526  io.mem_release.valid := false.B
527  io.mem_release.bits  := DontCare
528  io.mem_grant.ready   := false.B
529
530  // dalay data write in miss queue release update for 1 cycle
531  val release_update_bits_for_entry = Wire(new MissQueueEntryReleaseUpdate)
532  release_update_bits_for_entry.addr := io.release_update.bits.addr
533  release_update_bits_for_entry.mask_delayed := RegEnable(io.release_update.bits.mask, io.release_update.valid)
534  release_update_bits_for_entry.data_delayed := RegEnable(io.release_update.bits.data, io.release_update.valid)
535  release_update_bits_for_entry.mask_orr := io.release_update.bits.mask.orR
536
537  // delay data write in miss queue req for 1 cycle
538  val req_data = RegEnable(io.req.bits.toWritebackReqData(), io.req.valid)
539
540  require(isPow2(cfg.nMissEntries))
541  val grant_source = io.mem_grant.bits.source
542  val entries = Seq.fill(cfg.nReleaseEntries)(Module(new WritebackEntry(edge)))
543  entries.zipWithIndex.foreach {
544    case (entry, i) =>
545      val former_primary_ready = if(i == 0)
546        false.B
547      else
548        Cat((0 until i).map(j => entries(j).io.primary_ready)).orR
549      val entry_id = (i + releaseIdBase).U
550
551      entry.io.id := entry_id
552
553      // entry req
554      entry.io.req.valid := io.req.valid
555      primary_ready_vec(i)   := entry.io.primary_ready
556      secondary_ready_vec(i) := entry.io.secondary_ready
557      entry.io.req.bits  := io.req.bits
558      entry.io.req_data  := req_data
559
560      entry.io.primary_valid := alloc &&
561        !former_primary_ready &&
562        entry.io.primary_ready
563      entry.io.secondary_valid := io.req.valid && accept
564
565      entry.io.mem_grant.valid := (entry_id === grant_source) && io.mem_grant.valid
566      entry.io.mem_grant.bits  := io.mem_grant.bits
567
568      entry.io.release_wakeup := io.release_wakeup
569      entry.io.release_update.valid := io.release_update.valid
570      entry.io.release_update.bits := release_update_bits_for_entry // data write delayed
571  }
572
573  io.req_ready_dup.zipWithIndex.foreach { case (rdy, i) =>
574    rdy := Cat(entries.map(_.io.primary_ready_dup(i))).orR
575  }
576
577  assert(RegNext(!(io.mem_grant.valid && !io.mem_grant.ready)))
578  io.mem_grant.ready := true.B
579
580  val miss_req_conflict = VecInit(entries.map(e => e.io.block_addr.valid && e.io.block_addr.bits === io.miss_req.bits)).asUInt.orR
581  io.block_miss_req := io.miss_req.valid && miss_req_conflict
582
583  TLArbiter.robin(edge, io.mem_release, entries.map(_.io.mem_release):_*)
584
585  // sanity check
586  // print all input/output requests for debug purpose
587  // print req
588  when (io.req.fire()) {
589    io.req.bits.dump()
590  }
591
592  when (io.mem_release.fire()) {
593    io.mem_release.bits.dump
594  }
595
596  when (io.mem_grant.fire()) {
597    io.mem_grant.bits.dump
598  }
599
600  when (io.miss_req.valid) {
601    XSDebug("miss_req: addr: %x\n", io.miss_req.bits)
602  }
603
604  when (io.block_miss_req) {
605    XSDebug("block_miss_req\n")
606  }
607
608  // performance counters
609  XSPerfAccumulate("wb_req", io.req.fire())
610
611  val perfValidCount = RegNext(PopCount(entries.map(e => e.io.block_addr.valid)))
612  val perfEvents = Seq(
613    ("dcache_wbq_req      ", io.req.fire()),
614    ("dcache_wbq_1_4_valid", (perfValidCount < (cfg.nReleaseEntries.U/4.U))),
615    ("dcache_wbq_2_4_valid", (perfValidCount > (cfg.nReleaseEntries.U/4.U)) & (perfValidCount <= (cfg.nReleaseEntries.U/2.U))),
616    ("dcache_wbq_3_4_valid", (perfValidCount > (cfg.nReleaseEntries.U/2.U)) & (perfValidCount <= (cfg.nReleaseEntries.U*3.U/4.U))),
617    ("dcache_wbq_4_4_valid", (perfValidCount > (cfg.nReleaseEntries.U*3.U/4.U))),
618  )
619  generatePerfEvent()
620}
621