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