xref: /XiangShan/src/main/scala/xiangshan/frontend/icache/WayLookup.scala (revision 6c106319588f5988a282dc2e7c687a9d44e9c209)
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
18package xiangshan.frontend.icache
19
20import chisel3._
21import chisel3.util._
22import org.chipsalliance.cde.config.Parameters
23import utility._
24import xiangshan.cache.mmu.Pbmt
25import xiangshan.frontend.ExceptionType
26
27/* WayLookupEntry is for internal storage, while WayLookupInfo is for interface
28 * Notes:
29 *   1. there must be a flush (caused by guest page fault) after excp_tlb_gpf === true.B,
30 *      so, we need only the first excp_tlb_gpf and the corresponding gpaddr.
31 *      to save area, we separate those signals from WayLookupEntry and store only once.
32 */
33class WayLookupEntry(implicit p: Parameters) extends ICacheBundle {
34  val vSetIdx:        Vec[UInt] = Vec(PortNumber, UInt(idxBits.W))
35  val waymask:        Vec[UInt] = Vec(PortNumber, UInt(nWays.W))
36  val ptag:           Vec[UInt] = Vec(PortNumber, UInt(tagBits.W))
37  val itlb_exception: Vec[UInt] = Vec(PortNumber, UInt(ExceptionType.width.W))
38  val itlb_pbmt:      Vec[UInt] = Vec(PortNumber, UInt(Pbmt.width.W))
39  val meta_codes:     Vec[UInt] = Vec(PortNumber, UInt(ICacheMetaCodeBits.W))
40}
41
42class WayLookupGPFEntry(implicit p: Parameters) extends ICacheBundle {
43  // NOTE: we don't use GPAddrBits here, refer to ICacheMainPipe.scala L43-48 and PR#3795
44  val gpaddr:            UInt = UInt(PAddrBitsMax.W)
45  val isForVSnonLeafPTE: Bool = Bool()
46}
47
48class WayLookupInfo(implicit p: Parameters) extends ICacheBundle {
49  val entry = new WayLookupEntry
50  val gpf   = new WayLookupGPFEntry
51
52  // for compatibility
53  def vSetIdx:           Vec[UInt] = entry.vSetIdx
54  def waymask:           Vec[UInt] = entry.waymask
55  def ptag:              Vec[UInt] = entry.ptag
56  def itlb_exception:    Vec[UInt] = entry.itlb_exception
57  def itlb_pbmt:         Vec[UInt] = entry.itlb_pbmt
58  def meta_codes:        Vec[UInt] = entry.meta_codes
59  def gpaddr:            UInt      = gpf.gpaddr
60  def isForVSnonLeafPTE: Bool      = gpf.isForVSnonLeafPTE
61}
62
63class WayLookupInterface(implicit p: Parameters) extends ICacheBundle {
64  val flush:  Bool                       = Input(Bool())
65  val read:   DecoupledIO[WayLookupInfo] = DecoupledIO(new WayLookupInfo)
66  val write:  DecoupledIO[WayLookupInfo] = Flipped(DecoupledIO(new WayLookupInfo))
67  val update: Valid[ICacheMissResp]      = Flipped(ValidIO(new ICacheMissResp))
68}
69
70class WayLookup(implicit p: Parameters) extends ICacheModule with HasICacheECCHelper {
71  val io: WayLookupInterface = IO(new WayLookupInterface)
72
73  class WayLookupPtr extends CircularQueuePtr[WayLookupPtr](nWayLookupSize)
74  private object WayLookupPtr {
75    def apply(f: Bool, v: UInt): WayLookupPtr = {
76      val ptr = Wire(new WayLookupPtr)
77      ptr.flag  := f
78      ptr.value := v
79      ptr
80    }
81  }
82
83  private val entries  = RegInit(VecInit(Seq.fill(nWayLookupSize)(0.U.asTypeOf(new WayLookupEntry))))
84  private val readPtr  = RegInit(WayLookupPtr(false.B, 0.U))
85  private val writePtr = RegInit(WayLookupPtr(false.B, 0.U))
86
87  private val empty = readPtr === writePtr
88  private val full  = (readPtr.value === writePtr.value) && (readPtr.flag ^ writePtr.flag)
89
90  when(io.flush) {
91    writePtr.value := 0.U
92    writePtr.flag  := false.B
93  }.elsewhen(io.write.fire) {
94    writePtr := writePtr + 1.U
95  }
96
97  when(io.flush) {
98    readPtr.value := 0.U
99    readPtr.flag  := false.B
100  }.elsewhen(io.read.fire) {
101    readPtr := readPtr + 1.U
102  }
103
104  private val gpf_entry = RegInit(0.U.asTypeOf(Valid(new WayLookupGPFEntry)))
105  private val gpfPtr    = RegInit(WayLookupPtr(false.B, 0.U))
106  private val gpf_hit   = gpfPtr === readPtr && gpf_entry.valid
107
108  when(io.flush) {
109    // we don't need to reset gpfPtr, since the valid is actually gpf_entries.excp_tlb_gpf
110    gpf_entry.valid := false.B
111    gpf_entry.bits  := 0.U.asTypeOf(new WayLookupGPFEntry)
112  }
113
114  /**
115    ******************************************************************************
116    * update
117    ******************************************************************************
118    */
119  private val hits = Wire(Vec(nWayLookupSize, Bool()))
120  entries.zip(hits).foreach { case (entry, hit) =>
121    val hit_vec = Wire(Vec(PortNumber, Bool()))
122    (0 until PortNumber).foreach { i =>
123      val vset_same = (io.update.bits.vSetIdx === entry.vSetIdx(i)) && !io.update.bits.corrupt && io.update.valid
124      val ptag_same = getPhyTagFromBlk(io.update.bits.blkPaddr) === entry.ptag(i)
125      val way_same  = io.update.bits.waymask === entry.waymask(i)
126      when(vset_same) {
127        when(ptag_same) {
128          // miss -> hit
129          entry.waymask(i) := io.update.bits.waymask
130          // also update meta_codes
131          // NOTE: we have getPhyTagFromBlk(io.update.bits.blkPaddr) === entry.ptag(i),
132          //       so we can use entry.ptag(i) for better timing
133          entry.meta_codes(i) := encodeMetaECC(entry.ptag(i))
134        }.elsewhen(way_same) {
135          // data is overwritten: hit -> miss
136          entry.waymask(i) := 0.U
137          // don't care meta_codes, since it's not used for a missed request
138        }
139      }
140      hit_vec(i) := vset_same && (ptag_same || way_same)
141    }
142    hit := hit_vec.reduce(_ || _)
143  }
144
145  /**
146    ******************************************************************************
147    * read
148    ******************************************************************************
149    */
150  // if the entry is empty, but there is a valid write, we can bypass it to read port (maybe timing critical)
151  private val can_bypass = empty && io.write.valid
152  io.read.valid := !empty || io.write.valid
153  when(can_bypass) {
154    io.read.bits := io.write.bits
155  }.otherwise { // can't bypass
156    io.read.bits.entry := entries(readPtr.value)
157    when(gpf_hit) { // ptr match && entry valid
158      io.read.bits.gpf := gpf_entry.bits
159      // also clear gpf_entry.valid when it's read, note this will be overridden by write (L175)
160      when(io.read.fire) {
161        gpf_entry.valid := false.B
162      }
163    }.otherwise { // gpf not hit
164      io.read.bits.gpf := 0.U.asTypeOf(new WayLookupGPFEntry)
165    }
166  }
167
168  /**
169    ******************************************************************************
170    * write
171    ******************************************************************************
172    */
173  // if there is a valid gpf to be read, we should stall write
174  private val gpf_stall = gpf_entry.valid && !(io.read.fire && gpf_hit)
175  io.write.ready := !full && !gpf_stall
176  when(io.write.fire) {
177    entries(writePtr.value) := io.write.bits.entry
178    when(io.write.bits.itlb_exception.map(_ === ExceptionType.gpf).reduce(_ || _)) {
179      // if gpf_entry is bypassed, we don't need to save it
180      // note this will override the read (L156)
181      gpf_entry.valid := !(can_bypass && io.read.fire)
182      gpf_entry.bits  := io.write.bits.gpf
183      gpfPtr          := writePtr
184    }
185  }
186}
187