xref: /XiangShan/src/main/scala/xiangshan/frontend/icache/ICache.scala (revision c49ebec88f6e402aefec681225e3537e2c511430)
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    ))
259
260    // meta connection
261    if (bank == 0) {
262      tagArray.io.r.req.valid := port_0_read_0 || port_1_read_0
263      tagArray.io.r.req.bits.apply(setIdx = bank_0_idx(highestIdxBit, 1))
264      tagArray.io.w.req.valid := write_bank_0
265      tagArray.io.w.req.bits.apply(
266        data = write_meta_bits,
267        setIdx = io.write.bits.virIdx(highestIdxBit, 1),
268        waymask = io.write.bits.waymask
269      )
270    } else {
271      tagArray.io.r.req.valid := port_0_read_1 || port_1_read_1
272      tagArray.io.r.req.bits.apply(setIdx = bank_1_idx(highestIdxBit, 1))
273      tagArray.io.w.req.valid := write_bank_1
274      tagArray.io.w.req.bits.apply(
275        data = write_meta_bits,
276        setIdx = io.write.bits.virIdx(highestIdxBit, 1),
277        waymask = io.write.bits.waymask
278      )
279    }
280
281    tagArray
282  }
283
284  val read_set_idx_next = RegEnable(io.read.bits.vSetIdx, 0.U.asTypeOf(io.read.bits.vSetIdx), io.read.fire)
285  val valid_array       = RegInit(VecInit(Seq.fill(nWays)(0.U(nSets.W))))
286  val valid_metas       = Wire(Vec(PortNumber, Vec(nWays, Bool())))
287  // valid read
288  (0 until PortNumber).foreach(i =>
289    (0 until nWays).foreach(way =>
290      valid_metas(i)(way) := valid_array(way)(read_set_idx_next(i))
291    )
292  )
293  io.readResp.entryValid := valid_metas
294
295  io.read.ready := !io.write.valid && !io.fencei && tagArrays.map(_.io.r.req.ready).reduce(_ && _)
296
297  // valid write
298  val way_num = OHToUInt(io.write.bits.waymask)
299  when(io.write.valid) {
300    valid_array(way_num) := valid_array(way_num).bitSet(io.write.bits.virIdx, true.B)
301  }
302
303  XSPerfAccumulate("meta_refill_num", io.write.valid)
304
305  io.readResp.metas <> DontCare
306  io.readResp.codes <> DontCare
307  val readMetaEntries = tagArrays.map(port => port.io.r.resp.asTypeOf(Vec(nWays, new ICacheMetaEntry())))
308  val readMetas       = readMetaEntries.map(_.map(_.meta))
309  val readCodes       = readMetaEntries.map(_.map(_.code))
310
311  // TEST: force ECC to fail by setting readCodes to 0
312  if (ICacheForceMetaECCError) {
313    readCodes.foreach(_.foreach(_ := 0.U))
314  }
315
316  when(port_0_read_0_reg) {
317    io.readResp.metas(0) := readMetas(0)
318    io.readResp.codes(0) := readCodes(0)
319  }.elsewhen(port_0_read_1_reg) {
320    io.readResp.metas(0) := readMetas(1)
321    io.readResp.codes(0) := readCodes(1)
322  }
323
324  when(port_1_read_0_reg) {
325    io.readResp.metas(1) := readMetas(0)
326    io.readResp.codes(1) := readCodes(0)
327  }.elsewhen(port_1_read_1_reg) {
328    io.readResp.metas(1) := readMetas(1)
329    io.readResp.codes(1) := readCodes(1)
330  }
331
332  io.write.ready := true.B // TODO : has bug ? should be !io.cacheOp.req.valid
333
334  // fencei logic : reset valid_array
335  when(io.fencei) {
336    (0 until nWays).foreach(way =>
337      valid_array(way) := 0.U
338    )
339  }
340}
341
342class ICacheDataArray(implicit p: Parameters) extends ICacheArray {
343  class ICacheDataEntry(implicit p: Parameters) extends ICacheBundle {
344    val data = UInt(ICacheDataBits.W)
345    val code = UInt(ICacheDataCodeBits.W)
346  }
347
348  object ICacheDataEntry {
349    def apply(data: UInt)(implicit p: Parameters) = {
350      val entry = Wire(new ICacheDataEntry)
351      entry.data := data
352      entry.code := encodeDataECC(data)
353      entry
354    }
355  }
356
357  val io = IO {
358    new Bundle {
359      val write = Flipped(DecoupledIO(new ICacheDataWriteBundle))
360      // TODO: fix hard code
361      val read     = Flipped(Vec(4, DecoupledIO(new ICacheReadBundle)))
362      val readResp = Output(new ICacheDataRespBundle)
363    }
364  }
365
366  /**
367    ******************************************************************************
368    * data array
369    ******************************************************************************
370    */
371  val writeDatas   = io.write.bits.data.asTypeOf(Vec(ICacheDataBanks, UInt(ICacheDataBits.W)))
372  val writeEntries = writeDatas.map(ICacheDataEntry(_).asUInt)
373
374  val bankSel  = getBankSel(io.read(0).bits.blkOffset, io.read(0).valid)
375  val lineSel  = getLineSel(io.read(0).bits.blkOffset)
376  val waymasks = io.read(0).bits.wayMask
377  val masks    = Wire(Vec(nWays, Vec(ICacheDataBanks, Bool())))
378  (0 until nWays).foreach { way =>
379    (0 until ICacheDataBanks).foreach { bank =>
380      masks(way)(bank) := Mux(
381        lineSel(bank),
382        waymasks(1)(way) && bankSel(1)(bank).asBool,
383        waymasks(0)(way) && bankSel(0)(bank).asBool
384      )
385    }
386  }
387
388  val dataArrays = (0 until nWays).map { way =>
389    (0 until ICacheDataBanks).map { bank =>
390      val sramBank = Module(new SRAMTemplateWithFixedWidth(
391        UInt(ICacheDataEntryBits.W),
392        set = nSets,
393        width = ICacheDataSRAMWidth,
394        shouldReset = true,
395        holdRead = true,
396        singlePort = true
397      ))
398
399      // read
400      sramBank.io.r.req.valid := io.read(bank % 4).valid && masks(way)(bank)
401      sramBank.io.r.req.bits.apply(setIdx =
402        Mux(lineSel(bank), io.read(bank % 4).bits.vSetIdx(1), io.read(bank % 4).bits.vSetIdx(0))
403      )
404      // write
405      sramBank.io.w.req.valid := io.write.valid && io.write.bits.waymask(way).asBool
406      sramBank.io.w.req.bits.apply(
407        data = writeEntries(bank),
408        setIdx = io.write.bits.virIdx,
409        // waymask is invalid when way of SRAMTemplate <= 1
410        waymask = 0.U
411      )
412      sramBank
413    }
414  }
415
416  /**
417    ******************************************************************************
418    * read logic
419    ******************************************************************************
420    */
421  val masksReg = RegEnable(masks, 0.U.asTypeOf(masks), io.read(0).valid)
422  val readDataWithCode = (0 until ICacheDataBanks).map(bank =>
423    Mux1H(VecInit(masksReg.map(_(bank))).asTypeOf(UInt(nWays.W)), dataArrays.map(_(bank).io.r.resp.asUInt))
424  )
425  val readEntries = readDataWithCode.map(_.asTypeOf(new ICacheDataEntry()))
426  val readDatas   = VecInit(readEntries.map(_.data))
427  val readCodes   = VecInit(readEntries.map(_.code))
428
429  // TEST: force ECC to fail by setting readCodes to 0
430  if (ICacheForceDataECCError) {
431    readCodes.foreach(_ := 0.U)
432  }
433
434  /**
435    ******************************************************************************
436    * IO
437    ******************************************************************************
438    */
439  io.readResp.datas := readDatas
440  io.readResp.codes := readCodes
441  io.write.ready    := true.B
442  io.read.foreach(_.ready := !io.write.valid)
443}
444
445class ICacheReplacer(implicit p: Parameters) extends ICacheModule {
446  val io = IO(new Bundle {
447    val touch  = Vec(PortNumber, Flipped(ValidIO(new ReplacerTouch)))
448    val victim = Flipped(new ReplacerVictim)
449  })
450
451  val replacers = Seq.fill(PortNumber)(ReplacementPolicy.fromString(cacheParams.replacer, nWays, nSets / PortNumber))
452
453  // touch
454  val touch_sets = Seq.fill(PortNumber)(Wire(Vec(2, UInt(log2Ceil(nSets / 2).W))))
455  val touch_ways = Seq.fill(PortNumber)(Wire(Vec(2, Valid(UInt(log2Ceil(nWays).W)))))
456  (0 until PortNumber).foreach { i =>
457    touch_sets(i)(0) := Mux(
458      io.touch(i).bits.vSetIdx(0),
459      io.touch(1).bits.vSetIdx(highestIdxBit, 1),
460      io.touch(0).bits.vSetIdx(highestIdxBit, 1)
461    )
462    touch_ways(i)(0).bits  := Mux(io.touch(i).bits.vSetIdx(0), io.touch(1).bits.way, io.touch(0).bits.way)
463    touch_ways(i)(0).valid := Mux(io.touch(i).bits.vSetIdx(0), io.touch(1).valid, io.touch(0).valid)
464  }
465
466  // victim
467  io.victim.way := Mux(
468    io.victim.vSetIdx.bits(0),
469    replacers(1).way(io.victim.vSetIdx.bits(highestIdxBit, 1)),
470    replacers(0).way(io.victim.vSetIdx.bits(highestIdxBit, 1))
471  )
472
473  // touch the victim in next cycle
474  val victim_vSetIdx_reg =
475    RegEnable(io.victim.vSetIdx.bits, 0.U.asTypeOf(io.victim.vSetIdx.bits), io.victim.vSetIdx.valid)
476  val victim_way_reg = RegEnable(io.victim.way, 0.U.asTypeOf(io.victim.way), io.victim.vSetIdx.valid)
477  (0 until PortNumber).foreach { i =>
478    touch_sets(i)(1)       := victim_vSetIdx_reg(highestIdxBit, 1)
479    touch_ways(i)(1).bits  := victim_way_reg
480    touch_ways(i)(1).valid := RegNext(io.victim.vSetIdx.valid) && (victim_vSetIdx_reg(0) === i.U)
481  }
482
483  ((replacers zip touch_sets) zip touch_ways).map { case ((r, s), w) => r.access(s, w) }
484}
485
486class ICacheIO(implicit p: Parameters) extends ICacheBundle {
487  val hartId       = Input(UInt(hartIdLen.W))
488  val ftqPrefetch  = Flipped(new FtqToPrefetchIO)
489  val softPrefetch = Vec(backendParams.LduCnt, Flipped(Valid(new SoftIfetchPrefetchBundle)))
490  val stop         = Input(Bool())
491  val fetch        = new ICacheMainPipeBundle
492  val toIFU        = Output(Bool())
493  val pmp          = Vec(2 * PortNumber, new ICachePMPBundle)
494  val itlb         = Vec(PortNumber, new TlbRequestIO)
495  val perfInfo     = Output(new ICachePerfInfo)
496  val error        = ValidIO(new L1CacheErrorInfo)
497  /* CSR control signal */
498  val csr_pf_enable     = Input(Bool())
499  val csr_parity_enable = Input(Bool())
500  val fencei            = Input(Bool())
501  val flush             = Input(Bool())
502}
503
504class ICache()(implicit p: Parameters) extends LazyModule with HasICacheParameters {
505  override def shouldBeInlined: Boolean = false
506
507  val clientParameters = TLMasterPortParameters.v1(
508    Seq(TLMasterParameters.v1(
509      name = "icache",
510      sourceId = IdRange(0, cacheParams.nFetchMshr + cacheParams.nPrefetchMshr + 1)
511    )),
512    requestFields = cacheParams.reqFields,
513    echoFields = cacheParams.echoFields
514  )
515
516  val clientNode = TLClientNode(Seq(clientParameters))
517
518  lazy val module = new ICacheImp(this)
519}
520
521class ICacheImp(outer: ICache) extends LazyModuleImp(outer) with HasICacheParameters with HasPerfEvents {
522  val io = IO(new ICacheIO)
523
524  println("ICache:")
525  println("  TagECC: " + cacheParams.tagECC)
526  println("  DataECC: " + cacheParams.dataECC)
527  println("  ICacheSets: " + cacheParams.nSets)
528  println("  ICacheWays: " + cacheParams.nWays)
529  println("  PortNumber: " + cacheParams.PortNumber)
530  println("  nFetchMshr: " + cacheParams.nFetchMshr)
531  println("  nPrefetchMshr: " + cacheParams.nPrefetchMshr)
532  println("  nWayLookupSize: " + cacheParams.nWayLookupSize)
533  println("  DataCodeUnit: " + cacheParams.DataCodeUnit)
534  println("  ICacheDataBanks: " + cacheParams.ICacheDataBanks)
535  println("  ICacheDataSRAMWidth: " + cacheParams.ICacheDataSRAMWidth)
536
537  val (bus, edge) = outer.clientNode.out.head
538
539  val metaArray  = Module(new ICacheMetaArray)
540  val dataArray  = Module(new ICacheDataArray)
541  val mainPipe   = Module(new ICacheMainPipe)
542  val missUnit   = Module(new ICacheMissUnit(edge))
543  val replacer   = Module(new ICacheReplacer)
544  val prefetcher = Module(new IPrefetchPipe)
545  val wayLookup  = Module(new WayLookup)
546
547  dataArray.io.write <> missUnit.io.data_write
548  dataArray.io.read <> mainPipe.io.dataArray.toIData
549  dataArray.io.readResp <> mainPipe.io.dataArray.fromIData
550
551  metaArray.io.fencei := io.fencei
552  metaArray.io.write <> missUnit.io.meta_write
553  metaArray.io.read <> prefetcher.io.metaRead.toIMeta
554  metaArray.io.readResp <> prefetcher.io.metaRead.fromIMeta
555
556  prefetcher.io.flush             := io.flush
557  prefetcher.io.csr_pf_enable     := io.csr_pf_enable
558  prefetcher.io.csr_parity_enable := io.csr_parity_enable
559  prefetcher.io.MSHRResp          := missUnit.io.fetch_resp
560  prefetcher.io.flushFromBpu      := io.ftqPrefetch.flushFromBpu
561  // cache softPrefetch
562  private val softPrefetchValid = RegInit(false.B)
563  private val softPrefetch      = RegInit(0.U.asTypeOf(new IPrefetchReq))
564  /* FIXME:
565   * If there is already a pending softPrefetch request, it will be overwritten.
566   * Also, if there are multiple softPrefetch requests in the same cycle, only the first one will be accepted.
567   * We should implement a softPrefetchQueue (like ibuffer, multi-in, single-out) to solve this.
568   * However, the impact on performance still needs to be assessed.
569   * Considering that the frequency of prefetch.i may not be high, let's start with a temporary dummy solution.
570   */
571  when(io.softPrefetch.map(_.valid).reduce(_ || _)) {
572    softPrefetchValid := true.B
573    softPrefetch.fromSoftPrefetch(MuxCase(
574      0.U.asTypeOf(new SoftIfetchPrefetchBundle),
575      io.softPrefetch.map(req => req.valid -> req.bits)
576    ))
577  }.elsewhen(prefetcher.io.req.fire) {
578    softPrefetchValid := false.B
579  }
580  // pass ftqPrefetch
581  private val ftqPrefetch = WireInit(0.U.asTypeOf(new IPrefetchReq))
582  ftqPrefetch.fromFtqICacheInfo(io.ftqPrefetch.req.bits)
583  // software prefetch has higher priority
584  prefetcher.io.req.valid                 := softPrefetchValid || io.ftqPrefetch.req.valid
585  prefetcher.io.req.bits                  := Mux(softPrefetchValid, softPrefetch, ftqPrefetch)
586  prefetcher.io.req.bits.backendException := io.ftqPrefetch.backendException
587  io.ftqPrefetch.req.ready                := prefetcher.io.req.ready && !softPrefetchValid
588
589  missUnit.io.hartId := io.hartId
590  missUnit.io.fencei := io.fencei
591  missUnit.io.flush  := io.flush
592  missUnit.io.fetch_req <> mainPipe.io.mshr.req
593  missUnit.io.prefetch_req <> prefetcher.io.MSHRReq
594  missUnit.io.mem_grant.valid := false.B
595  missUnit.io.mem_grant.bits  := DontCare
596  missUnit.io.mem_grant <> bus.d
597
598  mainPipe.io.flush             := io.flush
599  mainPipe.io.respStall         := io.stop
600  mainPipe.io.csr_parity_enable := io.csr_parity_enable
601  mainPipe.io.hartId            := io.hartId
602  mainPipe.io.mshr.resp         := missUnit.io.fetch_resp
603  mainPipe.io.fetch.req <> io.fetch.req
604  mainPipe.io.wayLookupRead <> wayLookup.io.read
605
606  wayLookup.io.flush := io.flush
607  wayLookup.io.write <> prefetcher.io.wayLookupWrite
608  wayLookup.io.update := missUnit.io.fetch_resp
609
610  replacer.io.touch <> mainPipe.io.touch
611  replacer.io.victim <> missUnit.io.victim
612
613  io.pmp(0) <> mainPipe.io.pmp(0)
614  io.pmp(1) <> mainPipe.io.pmp(1)
615  io.pmp(2) <> prefetcher.io.pmp(0)
616  io.pmp(3) <> prefetcher.io.pmp(1)
617
618  io.itlb(0) <> prefetcher.io.itlb(0)
619  io.itlb(1) <> prefetcher.io.itlb(1)
620
621  // notify IFU that Icache pipeline is available
622  io.toIFU    := mainPipe.io.fetch.req.ready
623  io.perfInfo := mainPipe.io.perfInfo
624
625  io.fetch.resp <> mainPipe.io.fetch.resp
626  io.fetch.topdownIcacheMiss := mainPipe.io.fetch.topdownIcacheMiss
627  io.fetch.topdownItlbMiss   := mainPipe.io.fetch.topdownItlbMiss
628
629  bus.b.ready := false.B
630  bus.c.valid := false.B
631  bus.c.bits  := DontCare
632  bus.e.valid := false.B
633  bus.e.bits  := DontCare
634
635  bus.a <> missUnit.io.mem_acquire
636
637  // Parity error port
638  val errors       = mainPipe.io.errors
639  val errors_valid = errors.map(e => e.valid).reduce(_ | _)
640  io.error.bits <> RegEnable(
641    PriorityMux(errors.map(e => e.valid -> e.bits)),
642    0.U.asTypeOf(errors(0).bits),
643    errors_valid
644  )
645  io.error.valid := RegNext(errors_valid, false.B)
646
647  XSPerfAccumulate(
648    "softPrefetch_drop_not_ready",
649    io.softPrefetch.map(_.valid).reduce(_ || _) && softPrefetchValid && !prefetcher.io.req.fire
650  )
651  XSPerfAccumulate("softPrefetch_drop_multi_req", PopCount(io.softPrefetch.map(_.valid)) > 1.U)
652  XSPerfAccumulate("softPrefetch_block_ftq", softPrefetchValid && io.ftqPrefetch.req.valid)
653
654  val perfEvents = Seq(
655    ("icache_miss_cnt  ", false.B),
656    ("icache_miss_penalty", BoolStopWatch(start = false.B, stop = false.B || false.B, startHighPriority = true))
657  )
658  generatePerfEvent()
659}
660
661class ICachePartWayReadBundle[T <: Data](gen: T, pWay: Int)(implicit p: Parameters)
662    extends ICacheBundle {
663  val req = Flipped(Vec(
664    PortNumber,
665    Decoupled(new Bundle {
666      val ridx = UInt((log2Ceil(nSets) - 1).W)
667    })
668  ))
669  val resp = Output(new Bundle {
670    val rdata = Vec(PortNumber, Vec(pWay, gen))
671  })
672}
673
674class ICacheWriteBundle[T <: Data](gen: T, pWay: Int)(implicit p: Parameters)
675    extends ICacheBundle {
676  val wdata    = gen
677  val widx     = UInt((log2Ceil(nSets) - 1).W)
678  val wbankidx = Bool()
679  val wmask    = Vec(pWay, Bool())
680}
681
682class ICachePartWayArray[T <: Data](gen: T, pWay: Int)(implicit p: Parameters) extends ICacheArray {
683
684  // including part way data
685  val io = IO {
686    new Bundle {
687      val read  = new ICachePartWayReadBundle(gen, pWay)
688      val write = Flipped(ValidIO(new ICacheWriteBundle(gen, pWay)))
689    }
690  }
691
692  io.read.req.map(_.ready := !io.write.valid)
693
694  val srams = (0 until PortNumber) map { bank =>
695    val sramBank = Module(new SRAMTemplate(
696      gen,
697      set = nSets / 2,
698      way = pWay,
699      shouldReset = true,
700      holdRead = true,
701      singlePort = true
702    ))
703
704    sramBank.io.r.req.valid := io.read.req(bank).valid
705    sramBank.io.r.req.bits.apply(setIdx = io.read.req(bank).bits.ridx)
706
707    if (bank == 0) sramBank.io.w.req.valid := io.write.valid && !io.write.bits.wbankidx
708    else sramBank.io.w.req.valid           := io.write.valid && io.write.bits.wbankidx
709    sramBank.io.w.req.bits.apply(
710      data = io.write.bits.wdata,
711      setIdx = io.write.bits.widx,
712      waymask = io.write.bits.wmask.asUInt
713    )
714
715    sramBank
716  }
717
718  io.read.req.map(_.ready := !io.write.valid && srams.map(_.io.r.req.ready).reduce(_ && _))
719
720  io.read.resp.rdata := VecInit(srams.map(bank => bank.io.r.resp.asTypeOf(Vec(pWay, gen))))
721
722}
723
724// Automatically partition the SRAM based on the width of the data and the desired width.
725// final SRAM width = width * way
726class SRAMTemplateWithFixedWidth[T <: Data](
727    gen:         T,
728    set:         Int,
729    width:       Int,
730    way:         Int = 1,
731    shouldReset: Boolean = false,
732    holdRead:    Boolean = false,
733    singlePort:  Boolean = false,
734    bypassWrite: Boolean = false
735) extends Module {
736
737  val dataBits  = gen.getWidth
738  val bankNum   = math.ceil(dataBits.toDouble / width.toDouble).toInt
739  val totalBits = bankNum * width
740
741  val io = IO(new Bundle {
742    val r = Flipped(new SRAMReadBus(gen, set, way))
743    val w = Flipped(new SRAMWriteBus(gen, set, way))
744  })
745
746  val wordType = UInt(width.W)
747  val writeDatas = (0 until bankNum).map(bank =>
748    VecInit((0 until way).map(i =>
749      io.w.req.bits.data(i).asTypeOf(UInt(totalBits.W)).asTypeOf(Vec(bankNum, wordType))(bank)
750    ))
751  )
752
753  val srams = (0 until bankNum) map { bank =>
754    val sramBank = Module(new SRAMTemplate(
755      wordType,
756      set = set,
757      way = way,
758      shouldReset = shouldReset,
759      holdRead = holdRead,
760      singlePort = singlePort,
761      bypassWrite = bypassWrite
762    ))
763    // read req
764    sramBank.io.r.req.valid       := io.r.req.valid
765    sramBank.io.r.req.bits.setIdx := io.r.req.bits.setIdx
766
767    // write req
768    sramBank.io.w.req.valid       := io.w.req.valid
769    sramBank.io.w.req.bits.setIdx := io.w.req.bits.setIdx
770    sramBank.io.w.req.bits.data   := writeDatas(bank)
771    sramBank.io.w.req.bits.waymask.map(_ := io.w.req.bits.waymask.get)
772
773    sramBank
774  }
775
776  io.r.req.ready := !io.w.req.valid
777  (0 until way).foreach { i =>
778    io.r.resp.data(i) := VecInit((0 until bankNum).map(bank =>
779      srams(bank).io.r.resp.data(i)
780    )).asTypeOf(UInt(totalBits.W))(dataBits - 1, 0).asTypeOf(gen.cloneType)
781  }
782
783  io.r.req.ready := srams.head.io.r.req.ready
784  io.w.req.ready := srams.head.io.w.req.ready
785}
786