xref: /XiangShan/src/main/scala/xiangshan/frontend/icache/ICache.scala (revision 6639e9a467468f4e1b05a25a5de4500772aedeb1)
1/***************************************************************************************
2* Copyright (c) 2024 Beijing Institute of Open Source Chip (BOSC)
3* Copyright (c) 2020-2024 Institute of Computing Technology, Chinese Academy of Sciences
4* Copyright (c) 2020-2021 Peng Cheng Laboratory
5*
6* XiangShan is licensed under Mulan PSL v2.
7* You can use this software according to the terms and conditions of the Mulan PSL v2.
8* You may obtain a copy of Mulan PSL v2 at:
9*          http://license.coscl.org.cn/MulanPSL2
10*
11* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
12* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
13* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
14*
15* See the Mulan PSL v2 for more details.
16*
17*
18* Acknowledgement
19*
20* This implementation is inspired by several key papers:
21* [1] Glenn Reinman, Brad Calder, and Todd Austin. "[Fetch directed instruction prefetching.]
22* (https://doi.org/10.1109/MICRO.1999.809439)" 32nd Annual ACM/IEEE International Symposium on Microarchitecture
23* (MICRO). 1999.
24***************************************************************************************/
25
26package xiangshan.frontend.icache
27
28import chisel3._
29import chisel3.util._
30import freechips.rocketchip.diplomacy.IdRange
31import freechips.rocketchip.diplomacy.LazyModule
32import freechips.rocketchip.diplomacy.LazyModuleImp
33import freechips.rocketchip.tilelink._
34import freechips.rocketchip.util.BundleFieldBase
35import huancun.AliasField
36import huancun.PrefetchField
37import org.chipsalliance.cde.config.Parameters
38import utility._
39import utils._
40import xiangshan._
41import xiangshan.cache._
42import xiangshan.cache.mmu.TlbRequestIO
43import xiangshan.frontend._
44
45case class ICacheParameters(
46    nSets:               Int = 256,
47    nWays:               Int = 4,
48    rowBits:             Int = 64,
49    nTLBEntries:         Int = 32,
50    tagECC:              Option[String] = None,
51    dataECC:             Option[String] = None,
52    replacer:            Option[String] = Some("random"),
53    PortNumber:          Int = 2,
54    nFetchMshr:          Int = 4,
55    nPrefetchMshr:       Int = 10,
56    nWayLookupSize:      Int = 32,
57    DataCodeUnit:        Int = 64,
58    ICacheDataBanks:     Int = 8,
59    ICacheDataSRAMWidth: Int = 66,
60    // TODO: hard code, need delete
61    partWayNum: Int = 4,
62    nMMIOs:     Int = 1,
63    blockBytes: Int = 64
64) extends L1CacheParameters {
65
66  val setBytes     = nSets * blockBytes
67  val aliasBitsOpt = if (setBytes > pageSize) Some(log2Ceil(setBytes / pageSize)) else None
68  val reqFields: Seq[BundleFieldBase] = Seq(
69    PrefetchField(),
70    ReqSourceField()
71  ) ++ aliasBitsOpt.map(AliasField)
72  val echoFields: Seq[BundleFieldBase] = Nil
73  def tagCode:    Code                 = Code.fromString(tagECC)
74  def dataCode:   Code                 = Code.fromString(dataECC)
75  def replacement = ReplacementPolicy.fromString(replacer, nWays, nSets)
76}
77
78trait HasICacheParameters extends HasL1CacheParameters with HasInstrMMIOConst with HasIFUConst {
79  val cacheParams = icacheParameters
80
81  def ICacheSets          = cacheParams.nSets
82  def ICacheWays          = cacheParams.nWays
83  def PortNumber          = cacheParams.PortNumber
84  def nFetchMshr          = cacheParams.nFetchMshr
85  def nPrefetchMshr       = cacheParams.nPrefetchMshr
86  def nWayLookupSize      = cacheParams.nWayLookupSize
87  def DataCodeUnit        = cacheParams.DataCodeUnit
88  def ICacheDataBanks     = cacheParams.ICacheDataBanks
89  def ICacheDataSRAMWidth = cacheParams.ICacheDataSRAMWidth
90  def partWayNum          = cacheParams.partWayNum
91
92  def ICacheMetaBits      = tagBits // FIXME: unportable: maybe use somemethod to get width
93  def ICacheMetaCodeBits  = 1       // FIXME: unportable: maybe use cacheParams.tagCode.somemethod to get width
94  def ICacheMetaEntryBits = ICacheMetaBits + ICacheMetaCodeBits
95
96  def ICacheDataBits     = blockBits / ICacheDataBanks
97  def ICacheDataCodeSegs = math.ceil(ICacheDataBits / DataCodeUnit).toInt // split data to segments for ECC checking
98  def ICacheDataCodeBits =
99    ICacheDataCodeSegs * 1 // FIXME: unportable: maybe use cacheParams.dataCode.somemethod to get width
100  def ICacheDataEntryBits = ICacheDataBits + ICacheDataCodeBits
101  def ICacheBankVisitNum  = 32 * 8 / ICacheDataBits + 1
102  def highestIdxBit       = log2Ceil(nSets) - 1
103
104  require((ICacheDataBanks >= 2) && isPow2(ICacheDataBanks))
105  require(ICacheDataSRAMWidth >= ICacheDataEntryBits)
106  require(isPow2(ICacheSets), s"nSets($ICacheSets) must be pow2")
107  require(isPow2(ICacheWays), s"nWays($ICacheWays) must be pow2")
108
109  def getBits(num: Int) = log2Ceil(num).W
110
111  def generatePipeControl(lastFire: Bool, thisFire: Bool, thisFlush: Bool, lastFlush: Bool): Bool = {
112    val valid = RegInit(false.B)
113    when(thisFlush)(valid := false.B)
114      .elsewhen(lastFire && !lastFlush)(valid := true.B)
115      .elsewhen(thisFire)(valid := false.B)
116    valid
117  }
118
119  def ResultHoldBypass[T <: Data](data: T, valid: Bool): T =
120    Mux(valid, data, RegEnable(data, valid))
121
122  def ResultHoldBypass[T <: Data](data: T, init: T, valid: Bool): T =
123    Mux(valid, data, RegEnable(data, init, valid))
124
125  def holdReleaseLatch(valid: Bool, release: Bool, flush: Bool): Bool = {
126    val bit = RegInit(false.B)
127    when(flush)(bit := false.B)
128      .elsewhen(valid && !release)(bit := true.B)
129      .elsewhen(release)(bit := false.B)
130    bit || valid
131  }
132
133  def blockCounter(block: Bool, flush: Bool, threshold: Int): Bool = {
134    val counter = RegInit(0.U(log2Up(threshold + 1).W))
135    when(block)(counter := counter + 1.U)
136    when(flush)(counter := 0.U)
137    counter > threshold.U
138  }
139
140  def InitQueue[T <: Data](entry: T, size: Int): Vec[T] =
141    return RegInit(VecInit(Seq.fill(size)(0.U.asTypeOf(entry.cloneType))))
142
143  def encodeMetaECC(meta: UInt): UInt = {
144    require(meta.getWidth == ICacheMetaBits)
145    val code = cacheParams.tagCode.encode(meta) >> ICacheMetaBits
146    code.asTypeOf(UInt(ICacheMetaCodeBits.W))
147  }
148
149  def encodeDataECC(data: UInt): UInt = {
150    require(data.getWidth == ICacheDataBits)
151    val datas = data.asTypeOf(Vec(ICacheDataCodeSegs, UInt((ICacheDataBits / ICacheDataCodeSegs).W)))
152    val codes = VecInit(datas.map(cacheParams.dataCode.encode(_) >> (ICacheDataBits / ICacheDataCodeSegs)))
153    codes.asTypeOf(UInt(ICacheDataCodeBits.W))
154  }
155
156  def getBankSel(blkOffset: UInt, valid: Bool = true.B): Vec[UInt] = {
157    val bankIdxLow  = Cat(0.U(1.W), blkOffset) >> log2Ceil(blockBytes / ICacheDataBanks)
158    val bankIdxHigh = (Cat(0.U(1.W), blkOffset) + 32.U) >> log2Ceil(blockBytes / ICacheDataBanks)
159    val bankSel     = VecInit((0 until ICacheDataBanks * 2).map(i => (i.U >= bankIdxLow) && (i.U <= bankIdxHigh)))
160    assert(
161      !valid || PopCount(bankSel) === ICacheBankVisitNum.U,
162      "The number of bank visits must be %d, but bankSel=0x%x",
163      ICacheBankVisitNum.U,
164      bankSel.asUInt
165    )
166    bankSel.asTypeOf(UInt((ICacheDataBanks * 2).W)).asTypeOf(Vec(2, UInt(ICacheDataBanks.W)))
167  }
168
169  def getLineSel(blkOffset: UInt)(implicit p: Parameters): Vec[Bool] = {
170    val bankIdxLow = blkOffset >> log2Ceil(blockBytes / ICacheDataBanks)
171    val lineSel    = VecInit((0 until ICacheDataBanks).map(i => i.U < bankIdxLow))
172    lineSel
173  }
174
175  def getBlkAddr(addr:           UInt) = addr >> blockOffBits
176  def getPhyTagFromBlk(addr:     UInt): UInt = addr >> (pgUntagBits - blockOffBits)
177  def getIdxFromBlk(addr:        UInt) = addr(idxBits - 1, 0)
178  def get_paddr_from_ptag(vaddr: UInt, ptag: UInt) = Cat(ptag, vaddr(pgUntagBits - 1, 0))
179}
180
181abstract class ICacheBundle(implicit p: Parameters) extends XSBundle
182    with HasICacheParameters
183
184abstract class ICacheModule(implicit p: Parameters) extends XSModule
185    with HasICacheParameters
186
187abstract class ICacheArray(implicit p: Parameters) extends XSModule
188    with HasICacheParameters
189
190class ICacheMetadata(implicit p: Parameters) extends ICacheBundle {
191  val tag = UInt(tagBits.W)
192}
193
194object ICacheMetadata {
195  def apply(tag: Bits)(implicit p: Parameters) = {
196    val meta = Wire(new ICacheMetadata)
197    meta.tag := tag
198    meta
199  }
200}
201
202class ICacheMetaArray()(implicit p: Parameters) extends ICacheArray {
203  class ICacheMetaEntry(implicit p: Parameters) extends ICacheBundle {
204    val meta: ICacheMetadata = new ICacheMetadata
205    val code: UInt           = UInt(ICacheMetaCodeBits.W)
206  }
207
208  private object ICacheMetaEntry {
209    def apply(meta: ICacheMetadata)(implicit p: Parameters): ICacheMetaEntry = {
210      val entry = Wire(new ICacheMetaEntry)
211      entry.meta := meta
212      entry.code := encodeMetaECC(meta.asUInt)
213      entry
214    }
215  }
216
217  // sanity check
218  require(ICacheMetaEntryBits == (new ICacheMetaEntry).getWidth)
219
220  val io = IO(new Bundle {
221    val write    = Flipped(DecoupledIO(new ICacheMetaWriteBundle))
222    val read     = Flipped(DecoupledIO(new ICacheReadBundle))
223    val readResp = Output(new ICacheMetaRespBundle)
224    val fencei   = Input(Bool())
225  })
226
227  val port_0_read_0 = io.read.valid && !io.read.bits.vSetIdx(0)(0)
228  val port_0_read_1 = io.read.valid && io.read.bits.vSetIdx(0)(0)
229  val port_1_read_1 = io.read.valid && io.read.bits.vSetIdx(1)(0) && io.read.bits.isDoubleLine
230  val port_1_read_0 = io.read.valid && !io.read.bits.vSetIdx(1)(0) && io.read.bits.isDoubleLine
231
232  val port_0_read_0_reg = RegEnable(port_0_read_0, 0.U.asTypeOf(port_0_read_0), io.read.fire)
233  val port_0_read_1_reg = RegEnable(port_0_read_1, 0.U.asTypeOf(port_0_read_1), io.read.fire)
234  val port_1_read_1_reg = RegEnable(port_1_read_1, 0.U.asTypeOf(port_1_read_1), io.read.fire)
235  val port_1_read_0_reg = RegEnable(port_1_read_0, 0.U.asTypeOf(port_1_read_0), io.read.fire)
236
237  val bank_0_idx = Mux(port_0_read_0, io.read.bits.vSetIdx(0), io.read.bits.vSetIdx(1))
238  val bank_1_idx = Mux(port_0_read_1, io.read.bits.vSetIdx(0), io.read.bits.vSetIdx(1))
239  val bank_idx   = Seq(bank_0_idx, bank_1_idx)
240
241  val write_bank_0 = io.write.valid && !io.write.bits.bankIdx
242  val write_bank_1 = io.write.valid && io.write.bits.bankIdx
243
244  val write_meta_bits = ICacheMetaEntry(meta =
245    ICacheMetadata(
246      tag = io.write.bits.phyTag
247    )
248  )
249
250  val tagArrays = (0 until 2) map { bank =>
251    val tagArray = Module(new SRAMTemplate(
252      new ICacheMetaEntry(),
253      set = nSets / 2,
254      way = nWays,
255      shouldReset = true,
256      holdRead = true,
257      singlePort = true,
258      withClockGate = true
259    ))
260
261    // meta connection
262    if (bank == 0) {
263      tagArray.io.r.req.valid := port_0_read_0 || port_1_read_0
264      tagArray.io.r.req.bits.apply(setIdx = bank_0_idx(highestIdxBit, 1))
265      tagArray.io.w.req.valid := write_bank_0
266      tagArray.io.w.req.bits.apply(
267        data = write_meta_bits,
268        setIdx = io.write.bits.virIdx(highestIdxBit, 1),
269        waymask = io.write.bits.waymask
270      )
271    } else {
272      tagArray.io.r.req.valid := port_0_read_1 || port_1_read_1
273      tagArray.io.r.req.bits.apply(setIdx = bank_1_idx(highestIdxBit, 1))
274      tagArray.io.w.req.valid := write_bank_1
275      tagArray.io.w.req.bits.apply(
276        data = write_meta_bits,
277        setIdx = io.write.bits.virIdx(highestIdxBit, 1),
278        waymask = io.write.bits.waymask
279      )
280    }
281
282    tagArray
283  }
284
285  val read_set_idx_next = RegEnable(io.read.bits.vSetIdx, 0.U.asTypeOf(io.read.bits.vSetIdx), io.read.fire)
286  val valid_array       = RegInit(VecInit(Seq.fill(nWays)(0.U(nSets.W))))
287  val valid_metas       = Wire(Vec(PortNumber, Vec(nWays, Bool())))
288  // valid read
289  (0 until PortNumber).foreach(i =>
290    (0 until nWays).foreach(way =>
291      valid_metas(i)(way) := valid_array(way)(read_set_idx_next(i))
292    )
293  )
294  io.readResp.entryValid := valid_metas
295
296  io.read.ready := !io.write.valid && !io.fencei && tagArrays.map(_.io.r.req.ready).reduce(_ && _)
297
298  // valid write
299  val way_num = OHToUInt(io.write.bits.waymask)
300  when(io.write.valid) {
301    valid_array(way_num) := valid_array(way_num).bitSet(io.write.bits.virIdx, true.B)
302  }
303
304  XSPerfAccumulate("meta_refill_num", io.write.valid)
305
306  io.readResp.metas <> DontCare
307  io.readResp.codes <> DontCare
308  val readMetaEntries = tagArrays.map(port => port.io.r.resp.asTypeOf(Vec(nWays, new ICacheMetaEntry())))
309  val readMetas       = readMetaEntries.map(_.map(_.meta))
310  val readCodes       = readMetaEntries.map(_.map(_.code))
311
312  // TEST: force ECC to fail by setting readCodes to 0
313  if (ICacheForceMetaECCError) {
314    readCodes.foreach(_.foreach(_ := 0.U))
315  }
316
317  when(port_0_read_0_reg) {
318    io.readResp.metas(0) := readMetas(0)
319    io.readResp.codes(0) := readCodes(0)
320  }.elsewhen(port_0_read_1_reg) {
321    io.readResp.metas(0) := readMetas(1)
322    io.readResp.codes(0) := readCodes(1)
323  }
324
325  when(port_1_read_0_reg) {
326    io.readResp.metas(1) := readMetas(0)
327    io.readResp.codes(1) := readCodes(0)
328  }.elsewhen(port_1_read_1_reg) {
329    io.readResp.metas(1) := readMetas(1)
330    io.readResp.codes(1) := readCodes(1)
331  }
332
333  io.write.ready := true.B // TODO : has bug ? should be !io.cacheOp.req.valid
334
335  // fencei logic : reset valid_array
336  when(io.fencei) {
337    (0 until nWays).foreach(way =>
338      valid_array(way) := 0.U
339    )
340  }
341}
342
343class ICacheDataArray(implicit p: Parameters) extends ICacheArray {
344  class ICacheDataEntry(implicit p: Parameters) extends ICacheBundle {
345    val data = UInt(ICacheDataBits.W)
346    val code = UInt(ICacheDataCodeBits.W)
347  }
348
349  object ICacheDataEntry {
350    def apply(data: UInt)(implicit p: Parameters) = {
351      val entry = Wire(new ICacheDataEntry)
352      entry.data := data
353      entry.code := encodeDataECC(data)
354      entry
355    }
356  }
357
358  val io = IO {
359    new Bundle {
360      val write = Flipped(DecoupledIO(new ICacheDataWriteBundle))
361      // TODO: fix hard code
362      val read     = Flipped(Vec(4, DecoupledIO(new ICacheReadBundle)))
363      val readResp = Output(new ICacheDataRespBundle)
364    }
365  }
366
367  /**
368    ******************************************************************************
369    * data array
370    ******************************************************************************
371    */
372  val writeDatas   = io.write.bits.data.asTypeOf(Vec(ICacheDataBanks, UInt(ICacheDataBits.W)))
373  val writeEntries = writeDatas.map(ICacheDataEntry(_).asUInt)
374
375  val bankSel  = getBankSel(io.read(0).bits.blkOffset, io.read(0).valid)
376  val lineSel  = getLineSel(io.read(0).bits.blkOffset)
377  val waymasks = io.read(0).bits.wayMask
378  val masks    = Wire(Vec(nWays, Vec(ICacheDataBanks, Bool())))
379  (0 until nWays).foreach { way =>
380    (0 until ICacheDataBanks).foreach { bank =>
381      masks(way)(bank) := Mux(
382        lineSel(bank),
383        waymasks(1)(way) && bankSel(1)(bank).asBool,
384        waymasks(0)(way) && bankSel(0)(bank).asBool
385      )
386    }
387  }
388
389  val dataArrays = (0 until nWays).map { way =>
390    (0 until ICacheDataBanks).map { bank =>
391      val sramBank = Module(new SRAMTemplateWithFixedWidth(
392        UInt(ICacheDataEntryBits.W),
393        set = nSets,
394        width = ICacheDataSRAMWidth,
395        shouldReset = true,
396        holdRead = true,
397        singlePort = true,
398        withClockGate = true
399      ))
400
401      // read
402      sramBank.io.r.req.valid := io.read(bank % 4).valid && masks(way)(bank)
403      sramBank.io.r.req.bits.apply(setIdx =
404        Mux(lineSel(bank), io.read(bank % 4).bits.vSetIdx(1), io.read(bank % 4).bits.vSetIdx(0))
405      )
406      // write
407      sramBank.io.w.req.valid := io.write.valid && io.write.bits.waymask(way).asBool
408      sramBank.io.w.req.bits.apply(
409        data = writeEntries(bank),
410        setIdx = io.write.bits.virIdx,
411        // waymask is invalid when way of SRAMTemplate <= 1
412        waymask = 0.U
413      )
414      sramBank
415    }
416  }
417
418  /**
419    ******************************************************************************
420    * read logic
421    ******************************************************************************
422    */
423  val masksReg = RegEnable(masks, 0.U.asTypeOf(masks), io.read(0).valid)
424  val readDataWithCode = (0 until ICacheDataBanks).map(bank =>
425    Mux1H(VecInit(masksReg.map(_(bank))).asTypeOf(UInt(nWays.W)), dataArrays.map(_(bank).io.r.resp.asUInt))
426  )
427  val readEntries = readDataWithCode.map(_.asTypeOf(new ICacheDataEntry()))
428  val readDatas   = VecInit(readEntries.map(_.data))
429  val readCodes   = VecInit(readEntries.map(_.code))
430
431  // TEST: force ECC to fail by setting readCodes to 0
432  if (ICacheForceDataECCError) {
433    readCodes.foreach(_ := 0.U)
434  }
435
436  /**
437    ******************************************************************************
438    * IO
439    ******************************************************************************
440    */
441  io.readResp.datas := readDatas
442  io.readResp.codes := readCodes
443  io.write.ready    := true.B
444  io.read.foreach(_.ready := !io.write.valid)
445}
446
447class ICacheReplacer(implicit p: Parameters) extends ICacheModule {
448  val io = IO(new Bundle {
449    val touch  = Vec(PortNumber, Flipped(ValidIO(new ReplacerTouch)))
450    val victim = Flipped(new ReplacerVictim)
451  })
452
453  val replacers = Seq.fill(PortNumber)(ReplacementPolicy.fromString(cacheParams.replacer, nWays, nSets / PortNumber))
454
455  // touch
456  val touch_sets = Seq.fill(PortNumber)(Wire(Vec(2, UInt(log2Ceil(nSets / 2).W))))
457  val touch_ways = Seq.fill(PortNumber)(Wire(Vec(2, Valid(UInt(log2Ceil(nWays).W)))))
458  (0 until PortNumber).foreach { i =>
459    touch_sets(i)(0) := Mux(
460      io.touch(i).bits.vSetIdx(0),
461      io.touch(1).bits.vSetIdx(highestIdxBit, 1),
462      io.touch(0).bits.vSetIdx(highestIdxBit, 1)
463    )
464    touch_ways(i)(0).bits  := Mux(io.touch(i).bits.vSetIdx(0), io.touch(1).bits.way, io.touch(0).bits.way)
465    touch_ways(i)(0).valid := Mux(io.touch(i).bits.vSetIdx(0), io.touch(1).valid, io.touch(0).valid)
466  }
467
468  // victim
469  io.victim.way := Mux(
470    io.victim.vSetIdx.bits(0),
471    replacers(1).way(io.victim.vSetIdx.bits(highestIdxBit, 1)),
472    replacers(0).way(io.victim.vSetIdx.bits(highestIdxBit, 1))
473  )
474
475  // touch the victim in next cycle
476  val victim_vSetIdx_reg =
477    RegEnable(io.victim.vSetIdx.bits, 0.U.asTypeOf(io.victim.vSetIdx.bits), io.victim.vSetIdx.valid)
478  val victim_way_reg = RegEnable(io.victim.way, 0.U.asTypeOf(io.victim.way), io.victim.vSetIdx.valid)
479  (0 until PortNumber).foreach { i =>
480    touch_sets(i)(1)       := victim_vSetIdx_reg(highestIdxBit, 1)
481    touch_ways(i)(1).bits  := victim_way_reg
482    touch_ways(i)(1).valid := RegNext(io.victim.vSetIdx.valid) && (victim_vSetIdx_reg(0) === i.U)
483  }
484
485  ((replacers zip touch_sets) zip touch_ways).map { case ((r, s), w) => r.access(s, w) }
486}
487
488class ICacheIO(implicit p: Parameters) extends ICacheBundle {
489  val hartId       = Input(UInt(hartIdLen.W))
490  val ftqPrefetch  = Flipped(new FtqToPrefetchIO)
491  val softPrefetch = Vec(backendParams.LduCnt, Flipped(Valid(new SoftIfetchPrefetchBundle)))
492  val stop         = Input(Bool())
493  val fetch        = new ICacheMainPipeBundle
494  val toIFU        = Output(Bool())
495  val pmp          = Vec(2 * PortNumber, new ICachePMPBundle)
496  val itlb         = Vec(PortNumber, new TlbRequestIO)
497  val perfInfo     = Output(new ICachePerfInfo)
498  val error        = ValidIO(new L1CacheErrorInfo)
499  /* CSR control signal */
500  val csr_pf_enable     = Input(Bool())
501  val csr_parity_enable = Input(Bool())
502  val fencei            = Input(Bool())
503  val flush             = Input(Bool())
504}
505
506class ICache()(implicit p: Parameters) extends LazyModule with HasICacheParameters {
507  override def shouldBeInlined: Boolean = false
508
509  val clientParameters = TLMasterPortParameters.v1(
510    Seq(TLMasterParameters.v1(
511      name = "icache",
512      sourceId = IdRange(0, cacheParams.nFetchMshr + cacheParams.nPrefetchMshr + 1)
513    )),
514    requestFields = cacheParams.reqFields,
515    echoFields = cacheParams.echoFields
516  )
517
518  val clientNode = TLClientNode(Seq(clientParameters))
519
520  lazy val module = new ICacheImp(this)
521}
522
523class ICacheImp(outer: ICache) extends LazyModuleImp(outer) with HasICacheParameters with HasPerfEvents {
524  val io = IO(new ICacheIO)
525
526  println("ICache:")
527  println("  TagECC: " + cacheParams.tagECC)
528  println("  DataECC: " + cacheParams.dataECC)
529  println("  ICacheSets: " + cacheParams.nSets)
530  println("  ICacheWays: " + cacheParams.nWays)
531  println("  PortNumber: " + cacheParams.PortNumber)
532  println("  nFetchMshr: " + cacheParams.nFetchMshr)
533  println("  nPrefetchMshr: " + cacheParams.nPrefetchMshr)
534  println("  nWayLookupSize: " + cacheParams.nWayLookupSize)
535  println("  DataCodeUnit: " + cacheParams.DataCodeUnit)
536  println("  ICacheDataBanks: " + cacheParams.ICacheDataBanks)
537  println("  ICacheDataSRAMWidth: " + cacheParams.ICacheDataSRAMWidth)
538
539  val (bus, edge) = outer.clientNode.out.head
540
541  val metaArray  = Module(new ICacheMetaArray)
542  val dataArray  = Module(new ICacheDataArray)
543  val mainPipe   = Module(new ICacheMainPipe)
544  val missUnit   = Module(new ICacheMissUnit(edge))
545  val replacer   = Module(new ICacheReplacer)
546  val prefetcher = Module(new IPrefetchPipe)
547  val wayLookup  = Module(new WayLookup)
548
549  dataArray.io.write <> missUnit.io.data_write
550  dataArray.io.read <> mainPipe.io.dataArray.toIData
551  dataArray.io.readResp <> mainPipe.io.dataArray.fromIData
552
553  metaArray.io.fencei := io.fencei
554  metaArray.io.write <> missUnit.io.meta_write
555  metaArray.io.read <> prefetcher.io.metaRead.toIMeta
556  metaArray.io.readResp <> prefetcher.io.metaRead.fromIMeta
557
558  prefetcher.io.flush             := io.flush
559  prefetcher.io.csr_pf_enable     := io.csr_pf_enable
560  prefetcher.io.csr_parity_enable := io.csr_parity_enable
561  prefetcher.io.MSHRResp          := missUnit.io.fetch_resp
562  prefetcher.io.flushFromBpu      := io.ftqPrefetch.flushFromBpu
563  // cache softPrefetch
564  private val softPrefetchValid = RegInit(false.B)
565  private val softPrefetch      = RegInit(0.U.asTypeOf(new IPrefetchReq))
566  /* FIXME:
567   * If there is already a pending softPrefetch request, it will be overwritten.
568   * Also, if there are multiple softPrefetch requests in the same cycle, only the first one will be accepted.
569   * We should implement a softPrefetchQueue (like ibuffer, multi-in, single-out) to solve this.
570   * However, the impact on performance still needs to be assessed.
571   * Considering that the frequency of prefetch.i may not be high, let's start with a temporary dummy solution.
572   */
573  when(io.softPrefetch.map(_.valid).reduce(_ || _)) {
574    softPrefetchValid := true.B
575    softPrefetch.fromSoftPrefetch(MuxCase(
576      0.U.asTypeOf(new SoftIfetchPrefetchBundle),
577      io.softPrefetch.map(req => req.valid -> req.bits)
578    ))
579  }.elsewhen(prefetcher.io.req.fire) {
580    softPrefetchValid := false.B
581  }
582  // pass ftqPrefetch
583  private val ftqPrefetch = WireInit(0.U.asTypeOf(new IPrefetchReq))
584  ftqPrefetch.fromFtqICacheInfo(io.ftqPrefetch.req.bits)
585  // software prefetch has higher priority
586  prefetcher.io.req.valid                 := softPrefetchValid || io.ftqPrefetch.req.valid
587  prefetcher.io.req.bits                  := Mux(softPrefetchValid, softPrefetch, ftqPrefetch)
588  prefetcher.io.req.bits.backendException := io.ftqPrefetch.backendException
589  io.ftqPrefetch.req.ready                := prefetcher.io.req.ready && !softPrefetchValid
590
591  missUnit.io.hartId := io.hartId
592  missUnit.io.fencei := io.fencei
593  missUnit.io.flush  := io.flush
594  missUnit.io.fetch_req <> mainPipe.io.mshr.req
595  missUnit.io.prefetch_req <> prefetcher.io.MSHRReq
596  missUnit.io.mem_grant.valid := false.B
597  missUnit.io.mem_grant.bits  := DontCare
598  missUnit.io.mem_grant <> bus.d
599
600  mainPipe.io.flush             := io.flush
601  mainPipe.io.respStall         := io.stop
602  mainPipe.io.csr_parity_enable := io.csr_parity_enable
603  mainPipe.io.hartId            := io.hartId
604  mainPipe.io.mshr.resp         := missUnit.io.fetch_resp
605  mainPipe.io.fetch.req <> io.fetch.req
606  mainPipe.io.wayLookupRead <> wayLookup.io.read
607
608  wayLookup.io.flush := io.flush
609  wayLookup.io.write <> prefetcher.io.wayLookupWrite
610  wayLookup.io.update := missUnit.io.fetch_resp
611
612  replacer.io.touch <> mainPipe.io.touch
613  replacer.io.victim <> missUnit.io.victim
614
615  io.pmp(0) <> mainPipe.io.pmp(0)
616  io.pmp(1) <> mainPipe.io.pmp(1)
617  io.pmp(2) <> prefetcher.io.pmp(0)
618  io.pmp(3) <> prefetcher.io.pmp(1)
619
620  io.itlb(0) <> prefetcher.io.itlb(0)
621  io.itlb(1) <> prefetcher.io.itlb(1)
622
623  // notify IFU that Icache pipeline is available
624  io.toIFU    := mainPipe.io.fetch.req.ready
625  io.perfInfo := mainPipe.io.perfInfo
626
627  io.fetch.resp <> mainPipe.io.fetch.resp
628  io.fetch.topdownIcacheMiss := mainPipe.io.fetch.topdownIcacheMiss
629  io.fetch.topdownItlbMiss   := mainPipe.io.fetch.topdownItlbMiss
630
631  bus.b.ready := false.B
632  bus.c.valid := false.B
633  bus.c.bits  := DontCare
634  bus.e.valid := false.B
635  bus.e.bits  := DontCare
636
637  bus.a <> missUnit.io.mem_acquire
638
639  // Parity error port
640  val errors       = mainPipe.io.errors
641  val errors_valid = errors.map(e => e.valid).reduce(_ | _)
642  io.error.bits <> RegEnable(
643    PriorityMux(errors.map(e => e.valid -> e.bits)),
644    0.U.asTypeOf(errors(0).bits),
645    errors_valid
646  )
647  io.error.valid := RegNext(errors_valid, false.B)
648
649  XSPerfAccumulate(
650    "softPrefetch_drop_not_ready",
651    io.softPrefetch.map(_.valid).reduce(_ || _) && softPrefetchValid && !prefetcher.io.req.fire
652  )
653  XSPerfAccumulate("softPrefetch_drop_multi_req", PopCount(io.softPrefetch.map(_.valid)) > 1.U)
654  XSPerfAccumulate("softPrefetch_block_ftq", softPrefetchValid && io.ftqPrefetch.req.valid)
655
656  val perfEvents = Seq(
657    ("icache_miss_cnt  ", false.B),
658    ("icache_miss_penalty", BoolStopWatch(start = false.B, stop = false.B || false.B, startHighPriority = true))
659  )
660  generatePerfEvent()
661}
662
663class ICachePartWayReadBundle[T <: Data](gen: T, pWay: Int)(implicit p: Parameters)
664    extends ICacheBundle {
665  val req = Flipped(Vec(
666    PortNumber,
667    Decoupled(new Bundle {
668      val ridx = UInt((log2Ceil(nSets) - 1).W)
669    })
670  ))
671  val resp = Output(new Bundle {
672    val rdata = Vec(PortNumber, Vec(pWay, gen))
673  })
674}
675
676class ICacheWriteBundle[T <: Data](gen: T, pWay: Int)(implicit p: Parameters)
677    extends ICacheBundle {
678  val wdata    = gen
679  val widx     = UInt((log2Ceil(nSets) - 1).W)
680  val wbankidx = Bool()
681  val wmask    = Vec(pWay, Bool())
682}
683
684class ICachePartWayArray[T <: Data](gen: T, pWay: Int)(implicit p: Parameters) extends ICacheArray {
685
686  // including part way data
687  val io = IO {
688    new Bundle {
689      val read  = new ICachePartWayReadBundle(gen, pWay)
690      val write = Flipped(ValidIO(new ICacheWriteBundle(gen, pWay)))
691    }
692  }
693
694  io.read.req.map(_.ready := !io.write.valid)
695
696  val srams = (0 until PortNumber) map { bank =>
697    val sramBank = Module(new SRAMTemplate(
698      gen,
699      set = nSets / 2,
700      way = pWay,
701      shouldReset = true,
702      holdRead = true,
703      singlePort = true,
704      withClockGate = true
705    ))
706
707    sramBank.io.r.req.valid := io.read.req(bank).valid
708    sramBank.io.r.req.bits.apply(setIdx = io.read.req(bank).bits.ridx)
709
710    if (bank == 0) sramBank.io.w.req.valid := io.write.valid && !io.write.bits.wbankidx
711    else sramBank.io.w.req.valid           := io.write.valid && io.write.bits.wbankidx
712    sramBank.io.w.req.bits.apply(
713      data = io.write.bits.wdata,
714      setIdx = io.write.bits.widx,
715      waymask = io.write.bits.wmask.asUInt
716    )
717
718    sramBank
719  }
720
721  io.read.req.map(_.ready := !io.write.valid && srams.map(_.io.r.req.ready).reduce(_ && _))
722
723  io.read.resp.rdata := VecInit(srams.map(bank => bank.io.r.resp.asTypeOf(Vec(pWay, gen))))
724
725}
726
727// Automatically partition the SRAM based on the width of the data and the desired width.
728// final SRAM width = width * way
729class SRAMTemplateWithFixedWidth[T <: Data](
730    gen:           T,
731    set:           Int,
732    width:         Int,
733    way:           Int = 1,
734    shouldReset:   Boolean = false,
735    holdRead:      Boolean = false,
736    singlePort:    Boolean = false,
737    bypassWrite:   Boolean = false,
738    withClockGate: Boolean = false
739) extends Module {
740
741  val dataBits  = gen.getWidth
742  val bankNum   = math.ceil(dataBits.toDouble / width.toDouble).toInt
743  val totalBits = bankNum * width
744
745  val io = IO(new Bundle {
746    val r = Flipped(new SRAMReadBus(gen, set, way))
747    val w = Flipped(new SRAMWriteBus(gen, set, way))
748  })
749
750  val wordType = UInt(width.W)
751  val writeDatas = (0 until bankNum).map(bank =>
752    VecInit((0 until way).map(i =>
753      io.w.req.bits.data(i).asTypeOf(UInt(totalBits.W)).asTypeOf(Vec(bankNum, wordType))(bank)
754    ))
755  )
756
757  val srams = (0 until bankNum) map { bank =>
758    val sramBank = Module(new SRAMTemplate(
759      wordType,
760      set = set,
761      way = way,
762      shouldReset = shouldReset,
763      holdRead = holdRead,
764      singlePort = singlePort,
765      bypassWrite = bypassWrite,
766      withClockGate = withClockGate
767    ))
768    // read req
769    sramBank.io.r.req.valid       := io.r.req.valid
770    sramBank.io.r.req.bits.setIdx := io.r.req.bits.setIdx
771
772    // write req
773    sramBank.io.w.req.valid       := io.w.req.valid
774    sramBank.io.w.req.bits.setIdx := io.w.req.bits.setIdx
775    sramBank.io.w.req.bits.data   := writeDatas(bank)
776    sramBank.io.w.req.bits.waymask.map(_ := io.w.req.bits.waymask.get)
777
778    sramBank
779  }
780
781  io.r.req.ready := !io.w.req.valid
782  (0 until way).foreach { i =>
783    io.r.resp.data(i) := VecInit((0 until bankNum).map(bank =>
784      srams(bank).io.r.resp.data(i)
785    )).asTypeOf(UInt(totalBits.W))(dataBits - 1, 0).asTypeOf(gen.cloneType)
786  }
787
788  io.r.req.ready := srams.head.io.r.req.ready
789  io.w.req.ready := srams.head.io.w.req.ready
790}
791