xref: /XiangShan/src/main/scala/xiangshan/frontend/icache/ICacheMissUnit.scala (revision bb2f3f51dd67f6e16e0cc1ffe43368c9fc7e4aef)
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.frontend.icache
18
19import org.chipsalliance.cde.config.Parameters
20import chisel3._
21import chisel3.util._
22import freechips.rocketchip.diplomacy.IdRange
23import freechips.rocketchip.tilelink.ClientStates._
24import freechips.rocketchip.tilelink.TLPermissions._
25import freechips.rocketchip.tilelink._
26import xiangshan._
27import xiangshan.cache._
28import utils._
29import utility._
30import difftest._
31
32
33abstract class ICacheMissUnitModule(implicit p: Parameters) extends XSModule
34  with HasICacheParameters
35
36abstract class ICacheMissUnitBundle(implicit p: Parameters) extends XSBundle
37  with HasICacheParameters
38
39
40class Demultiplexer[T <: Data](val gen: T, val n: Int) extends Module
41{
42  /** Hardware module that is used to sequence 1 producers into n consumer.
43    * Priority is given to lower producer.
44    */
45  require(n >= 2)
46  val io = IO(new Bundle {
47    val in      = Flipped(DecoupledIO(gen))
48    val out     = Vec(n, DecoupledIO(gen))
49    val chosen  = Output(UInt(log2Ceil(n).W))
50  })
51
52  val grant = false.B +: (1 until n).map(i=> (0 until i).map(io.out(_).ready).reduce(_||_))
53  for (i <- 0 until n) {
54    io.out(i).bits := io.in.bits
55    io.out(i).valid := !grant(i) && io.in.valid
56  }
57
58  io.in.ready := grant.last || io.out.last.ready
59  io.chosen := PriorityEncoder(VecInit(io.out.map(_.ready)))
60}
61
62
63class MuxBundle[T <: Data](val gen: T, val n: Int) extends Module
64{
65  require(n >= 2)
66  val io = IO(new Bundle {
67    val sel     = Input(UInt(log2Ceil(n).W))
68    val in      = Flipped(Vec(n, DecoupledIO(gen)))
69    val out     = DecoupledIO(gen)
70  })
71
72  io.in   <> DontCare
73  io.out  <> DontCare
74  for (i <- 0 until n) {
75    when(io.sel === i.U) {
76      io.out <> io.in(i)
77    }
78    io.in(i).ready := (io.sel === i.U) && io.out.ready
79  }
80}
81
82
83class ICacheMissReq(implicit p: Parameters) extends ICacheBundle {
84  val blkPaddr  = UInt((PAddrBits - blockOffBits).W)
85  val vSetIdx   = UInt(idxBits.W)
86}
87
88
89class ICacheMissResp(implicit p: Parameters) extends ICacheBundle {
90  val blkPaddr  = UInt((PAddrBits - blockOffBits).W)
91  val vSetIdx   = UInt(idxBits.W)
92  val waymask   = UInt(nWays.W)
93  val data      = UInt(blockBits.W)
94  val corrupt   = Bool()
95}
96
97
98class LookUpMSHR(implicit p: Parameters) extends ICacheBundle {
99  val info  = ValidIO(new ICacheMissReq)
100  val hit   = Input(Bool())
101}
102
103
104class MSHRResp(implicit p: Parameters) extends ICacheBundle {
105  val blkPaddr  = UInt((PAddrBits - blockOffBits).W)
106  val vSetIdx   = UInt(idxBits.W)
107  val waymask   = UInt(log2Ceil(nWays).W)
108}
109
110
111class MSHRAcquire(edge: TLEdgeOut)(implicit p: Parameters) extends ICacheBundle {
112  val acquire = new TLBundleA(edge.bundle)
113  val vSetIdx = UInt(idxBits.W)
114}
115
116class ICacheMSHR(edge: TLEdgeOut, isFetch: Boolean, ID: Int)(implicit p: Parameters) extends ICacheMissUnitModule {
117  val io = IO(new Bundle {
118    val fencei      = Input(Bool())
119    val flush       = Input(Bool())
120    val invalid     = Input(Bool())
121    val req         = Flipped(DecoupledIO(new ICacheMissReq))
122    val acquire     = DecoupledIO(new MSHRAcquire(edge))
123    val lookUps     = Flipped(Vec(2, new LookUpMSHR))
124    val resp        = ValidIO(new MSHRResp)
125    val victimWay   = Input(UInt(log2Ceil(nWays).W))
126  })
127
128  val valid     = RegInit(Bool(), false.B)
129  // this MSHR doesn't respones to fetch and sram
130  val flush     = RegInit(Bool(), false.B)
131  val fencei    = RegInit(Bool(), false.B)
132  // this MSHR has been issued
133  val issue     = RegInit(Bool(), false.B)
134
135  val blkPaddr  = RegInit(UInt((PAddrBits - blockOffBits).W), 0.U)
136  val vSetIdx   = RegInit(UInt(idxBits.W), 0.U)
137  val waymask   = RegInit(UInt(log2Ceil(nWays).W), 0.U)
138
139  // look up and return result at the same cycle
140  val hits = io.lookUps.map(lookup => valid && !fencei && !flush && (lookup.info.bits.vSetIdx === vSetIdx) &&
141                                      (lookup.info.bits.blkPaddr === blkPaddr))
142  // Decoupling valid and bits
143  (0 until 2).foreach { i =>
144    io.lookUps(i).hit := hits(i)
145  }
146
147  // disable wake up when hit MSHR (fencei is low)
148  // when(hit) {
149  //   flush := false.B
150  // }
151
152  // invalid when the req hasn't been issued
153  when(io.fencei || io.flush) {
154    fencei  := true.B
155    flush   := true.B
156    when(!issue) {
157      valid := false.B
158    }
159  }
160
161  // receive request and register
162  io.req.ready := !valid && !io.flush && !io.fencei
163  when(io.req.fire) {
164    valid     := true.B
165    flush     := false.B
166    issue     := false.B
167    fencei    := false.B
168    blkPaddr  := io.req.bits.blkPaddr
169    vSetIdx   := io.req.bits.vSetIdx
170  }
171
172  // send request to L2
173  io.acquire.valid := valid && !issue && !io.flush && !io.fencei
174  val getBlock = edge.Get(
175    fromSource  = ID.U,
176    toAddress   = Cat(blkPaddr, 0.U(blockOffBits.W)),
177    lgSize      = (log2Up(cacheParams.blockBytes)).U
178  )._2
179  io.acquire.bits.acquire := getBlock
180  io.acquire.bits.acquire.user.lift(ReqSourceKey).foreach(_ := MemReqSource.CPUInst.id.U)
181  io.acquire.bits.vSetIdx := vSetIdx
182
183  // get victim way when acquire fire
184  when(io.acquire.fire) {
185    issue   := true.B
186    waymask := io.victimWay
187  }
188
189  // invalid request when grant finish
190  when(io.invalid) {
191    valid := false.B
192  }
193
194  // offer the information other than data for write sram and response fetch
195  io.resp.valid         := valid && (!flush && !fencei)
196  io.resp.bits.blkPaddr := blkPaddr
197  io.resp.bits.vSetIdx  := vSetIdx
198  io.resp.bits.waymask  := waymask
199}
200
201class ICacheMissBundle(edge: TLEdgeOut)(implicit p: Parameters) extends ICacheBundle{
202  // difftest
203  val hartId      = Input(Bool())
204  // control
205  val fencei      = Input(Bool())
206  val flush       = Input(Bool())
207  // fetch
208  val fetch_req     = Flipped(DecoupledIO(new ICacheMissReq))
209  val fetch_resp    = ValidIO(new ICacheMissResp)
210  // prefetch
211  val prefetch_req      = Flipped(DecoupledIO(new ICacheMissReq))
212  // SRAM Write Req
213  val meta_write    = DecoupledIO(new ICacheMetaWriteBundle)
214  val data_write    = DecoupledIO(new ICacheDataWriteBundle)
215  // get victim from replacer
216  val victim        = new ReplacerVictim
217  // Tilelink
218  val mem_acquire = DecoupledIO(new TLBundleA(edge.bundle))
219  val mem_grant   = Flipped(DecoupledIO(new TLBundleD(edge.bundle)))
220}
221
222
223class ICacheMissUnit(edge: TLEdgeOut)(implicit p: Parameters) extends ICacheMissUnitModule
224{
225  val io = IO(new ICacheMissBundle(edge))
226
227  /**
228    ******************************************************************************
229    * fetch have higher priority
230    * fetch MSHR: lower index have a higher priority
231    * prefetch MSHR: the prefetchMSHRs earlier have a higher priority
232    *                 ---------       --------------       -----------
233    * ---fetch reg--->| Demux |-----> | fetch MSHR |------>| Arbiter |---acquire--->
234    *                 ---------       --------------       -----------
235    *                                 | fetch MSHR |            ^
236    *                                 --------------            |
237    *                                                           |
238    *                                -----------------          |
239    *                                | prefetch MSHR |          |
240    *                 ---------      -----------------     -----------
241    * ---fetch reg--->| Demux |----> | prefetch MSHR |---->| Arbiter |
242    *                 ---------      -----------------     -----------
243    *                                |    .......    |
244    *                                -----------------
245    ******************************************************************************
246    */
247
248  val fetchDemux    = Module(new Demultiplexer(new ICacheMissReq, nFetchMshr))
249  val prefetchDemux = Module(new Demultiplexer(new ICacheMissReq, nPrefetchMshr))
250  val prefetchArb   = Module(new MuxBundle(new MSHRAcquire(edge), nPrefetchMshr))
251  val acquireArb    = Module(new Arbiter(new MSHRAcquire(edge), nFetchMshr + 1))
252
253  // To avoid duplicate request reception.
254  val fetchHit, prefetchHit   = Wire(Bool())
255  fetchDemux.io.in            <> io.fetch_req
256  fetchDemux.io.in.valid      := io.fetch_req.valid && !fetchHit
257  io.fetch_req.ready          := fetchDemux.io.in.ready || fetchHit
258  prefetchDemux.io.in         <> io.prefetch_req
259  prefetchDemux.io.in.valid   := io.prefetch_req.valid && !prefetchHit
260  io.prefetch_req.ready       := prefetchDemux.io.in.ready || prefetchHit
261  acquireArb.io.in.last       <> prefetchArb.io.out
262
263  // mem_acquire connect
264  io.mem_acquire.valid        := acquireArb.io.out.valid
265  io.mem_acquire.bits         := acquireArb.io.out.bits.acquire
266  acquireArb.io.out.ready     := io.mem_acquire.ready
267
268  val fetchMSHRs = (0 until nFetchMshr).map { i =>
269    val mshr = Module(new ICacheMSHR(edge, true, i))
270    mshr.io.flush   := false.B
271    mshr.io.fencei  := io.fencei
272    mshr.io.req <> fetchDemux.io.out(i)
273    mshr.io.lookUps(0).info.valid := io.fetch_req.valid
274    mshr.io.lookUps(0).info.bits  := io.fetch_req.bits
275    mshr.io.lookUps(1).info.valid := io.prefetch_req.valid
276    mshr.io.lookUps(1).info.bits  := io.prefetch_req.bits
277    mshr.io.victimWay             := io.victim.way
278    acquireArb.io.in(i) <> mshr.io.acquire
279    mshr
280  }
281
282  val prefetchMSHRs = (0 until nPrefetchMshr).map { i =>
283    val mshr = Module(new ICacheMSHR(edge, false, nFetchMshr + i))
284    mshr.io.flush := io.flush
285    mshr.io.fencei  := io.fencei
286    mshr.io.req <> prefetchDemux.io.out(i)
287    mshr.io.lookUps(0).info.valid := io.fetch_req.valid
288    mshr.io.lookUps(0).info.bits  := io.fetch_req.bits
289    mshr.io.lookUps(1).info.valid := io.prefetch_req.valid
290    mshr.io.lookUps(1).info.bits  := io.prefetch_req.bits
291    mshr.io.victimWay             := io.victim.way
292    prefetchArb.io.in(i) <> mshr.io.acquire
293    mshr
294  }
295
296  /**
297    ******************************************************************************
298    * MSHR look up
299    * - look up all mshr
300    ******************************************************************************
301    */
302  val allMSHRs = (fetchMSHRs ++ prefetchMSHRs)
303  val prefetchHitFetchReq = (io.prefetch_req.bits.blkPaddr === io.fetch_req.bits.blkPaddr) &&
304                            (io.prefetch_req.bits.vSetIdx === io.fetch_req.bits.vSetIdx) &&
305                            io.fetch_req.valid
306  fetchHit    := allMSHRs.map(mshr => mshr.io.lookUps(0).hit).reduce(_||_)
307  prefetchHit := allMSHRs.map(mshr => mshr.io.lookUps(1).hit).reduce(_||_) || prefetchHitFetchReq
308
309  /**
310    ******************************************************************************
311    * prefetchMSHRs priority
312    * - The requests that enter the prefetchMSHRs earlier have a higher priority in issuing.
313    * - The order of enqueuing is recorded in FIFO when requset enters MSHRs.
314    * - The requests are dispatched in the order they are recorded in FIFO.
315    ******************************************************************************
316    */
317  // When the FIFO is full, enqueue and dequeue operations do not occur at the same cycle.
318  // So the depth of the FIFO is set to match the number of MSHRs.
319  // val priorityFIFO = Module(new Queue(UInt(log2Ceil(nPrefetchMshr).W), nPrefetchMshr, hasFlush=true))
320  val priorityFIFO = Module(new FIFOReg(UInt(log2Ceil(nPrefetchMshr).W), nPrefetchMshr, hasFlush=true))
321  priorityFIFO.io.flush.get := io.flush || io.fencei
322  priorityFIFO.io.enq.valid := prefetchDemux.io.in.fire
323  priorityFIFO.io.enq.bits  := prefetchDemux.io.chosen
324  priorityFIFO.io.deq.ready := prefetchArb.io.out.fire
325  prefetchArb.io.sel        := priorityFIFO.io.deq.bits
326  assert(!(priorityFIFO.io.enq.fire ^ prefetchDemux.io.in.fire), "priorityFIFO.io.enq and io.prefetch_req must fire at the same cycle")
327  assert(!(priorityFIFO.io.deq.fire ^ prefetchArb.io.out.fire), "priorityFIFO.io.deq and prefetchArb.io.out must fire at the same cycle")
328
329  /**
330    ******************************************************************************
331    * Tilelink D channel (grant)
332    ******************************************************************************
333    */
334  //cacheline register
335  val readBeatCnt = RegInit(UInt(log2Up(refillCycles).W), 0.U)
336  val respDataReg = RegInit(VecInit(Seq.fill(refillCycles)(0.U(beatBits.W))))
337
338  val wait_last = readBeatCnt === (refillCycles - 1).U
339  when(io.mem_grant.fire && edge.hasData(io.mem_grant.bits)) {
340    respDataReg(readBeatCnt) := io.mem_grant.bits.data
341    readBeatCnt := Mux(wait_last || io.mem_grant.bits.corrupt, 0.U, readBeatCnt + 1.U)
342  }
343
344  // last transition finsh or corrupt
345  val last_fire = io.mem_grant.fire && edge.hasData(io.mem_grant.bits) &&
346                  (wait_last || io.mem_grant.bits.corrupt)
347
348  val (_, _, refill_done, _) = edge.addr_inc(io.mem_grant)
349  assert(!(refill_done ^ last_fire), "refill not done!")
350  io.mem_grant.ready := true.B
351
352  val last_fire_r = RegNext(last_fire)
353  val id_r        = RegNext(io.mem_grant.bits.source)
354  val corrupt_r   = RegNext(io.mem_grant.bits.corrupt)
355
356  /**
357    ******************************************************************************
358    * invalid mshr when finish transition
359    ******************************************************************************
360    */
361  (0 until (nFetchMshr + nPrefetchMshr)).foreach{ i =>
362    allMSHRs(i).io.invalid := last_fire_r && (id_r === i.U)
363  }
364
365  /**
366    ******************************************************************************
367    * response fetch and write SRAM
368    ******************************************************************************
369    */
370  // get request information from MSHRs
371  val allMSHRs_resp = VecInit(allMSHRs.map(mshr => mshr.io.resp))
372  val mshr_resp = allMSHRs_resp(id_r)
373
374  // get waymask from replacer when acquire fire
375  io.victim.vSetIdx.valid := acquireArb.io.out.valid
376  io.victim.vSetIdx.bits  := acquireArb.io.out.bits.vSetIdx
377  val waymask = UIntToOH(mshr_resp.bits.waymask)
378  val fetch_resp_valid = mshr_resp.valid && last_fire_r && !io.flush && !io.fencei
379  val write_sram_valid = fetch_resp_valid && !corrupt_r
380
381  // write SRAM
382  io.meta_write.bits.generate(tag     = getPhyTagFromBlk(mshr_resp.bits.blkPaddr),
383                              idx     = mshr_resp.bits.vSetIdx,
384                              waymask = waymask,
385                              bankIdx = mshr_resp.bits.vSetIdx(0))
386  io.data_write.bits.generate(data    = respDataReg.asUInt,
387                              idx     = mshr_resp.bits.vSetIdx,
388                              waymask = waymask,
389                              bankIdx = mshr_resp.bits.vSetIdx(0))
390
391  io.meta_write.valid := write_sram_valid
392  io.data_write.valid := write_sram_valid
393
394  // response fetch
395  io.fetch_resp.valid         := fetch_resp_valid
396  io.fetch_resp.bits.blkPaddr := mshr_resp.bits.blkPaddr
397  io.fetch_resp.bits.vSetIdx  := mshr_resp.bits.vSetIdx
398  io.fetch_resp.bits.waymask  := waymask
399  io.fetch_resp.bits.data     := respDataReg.asUInt
400  io.fetch_resp.bits.corrupt  := corrupt_r
401
402  /**
403    ******************************************************************************
404    * performance counter
405    ******************************************************************************
406    */
407  // Duplicate requests will be excluded.
408  XSPerfAccumulate("enq_fetch_req",     fetchDemux.io.in.fire)
409  XSPerfAccumulate("enq_prefetch_req",  prefetchDemux.io.in.fire)
410
411  /**
412    ******************************************************************************
413    * ChiselDB: record ICache SRAM write log
414    ******************************************************************************
415    */
416  class ICacheSRAMDB(implicit p: Parameters) extends ICacheBundle{
417    val blkPaddr  = UInt((PAddrBits - blockOffBits).W)
418    val vSetIdx   = UInt(idxBits.W)
419    val waymask   = UInt(log2Ceil(nWays).W)
420  }
421
422  val isWriteICacheSRAMTable = WireInit(Constantin.createRecord("isWriteICacheSRAMTable" + p(XSCoreParamsKey).HartId.toString))
423  val ICacheSRAMTable = ChiselDB.createTable("ICacheSRAMTable" + p(XSCoreParamsKey).HartId.toString, new ICacheSRAMDB)
424
425  val ICacheSRAMDBDumpData = Wire(new ICacheSRAMDB)
426  ICacheSRAMDBDumpData.blkPaddr  := mshr_resp.bits.blkPaddr
427  ICacheSRAMDBDumpData.vSetIdx   := mshr_resp.bits.vSetIdx
428  ICacheSRAMDBDumpData.waymask   := OHToUInt(waymask)
429  ICacheSRAMTable.log(
430    data  = ICacheSRAMDBDumpData,
431    en    = write_sram_valid,
432    clock = clock,
433    reset = reset
434  )
435
436  /**
437    ******************************************************************************
438    * Difftest
439    ******************************************************************************
440    */
441  if (env.EnableDifftest) {
442    val difftest = DifftestModule(new DiffRefillEvent, dontCare = true)
443    difftest.coreid := io.hartId
444    difftest.index  := 0.U
445    difftest.valid  := write_sram_valid
446    difftest.addr   := Cat(mshr_resp.bits.blkPaddr, 0.U(blockOffBits.W))
447    difftest.data   := respDataReg.asTypeOf(difftest.data)
448    difftest.idtfr  := DontCare
449  }
450}