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* 17* Acknowledgement 18* 19* This implementation is inspired by several key papers: 20* [1] George Z. Chrysos, and Joel S. Emer. "[Memory dependence prediction using store sets.] 21* (https://doi.org/10.1109/ISCA.1998.694770)" 25th Annual International Symposium on Computer Architecture (ISCA). 22* 1998. 23***************************************************************************************/ 24 25package xiangshan.mem.mdp 26 27import org.chipsalliance.cde.config.Parameters 28import chisel3._ 29import chisel3.util._ 30import xiangshan._ 31import utils._ 32import utility._ 33import xiangshan.backend.rob.RobPtr 34import xiangshan.backend.Bundles.DynInst 35 36// store set load violation predictor 37// See "Memory Dependence Prediction using Store Sets" for details 38 39// Store Set Identifier Table Entry 40class SSITEntry(implicit p: Parameters) extends XSBundle { 41 val valid = Bool() 42 val ssid = UInt(SSIDWidth.W) // store set identifier 43 val strict = Bool() // strict load wait is needed 44} 45 46// Store Set Identifier Table Entry 47class SSITDataEntry(implicit p: Parameters) extends XSBundle { 48 val ssid = UInt(SSIDWidth.W) // store set identifier 49 val strict = Bool() // strict load wait is needed 50} 51 52// Store Set Identifier Table 53class SSIT(implicit p: Parameters) extends XSModule { 54 val io = IO(new Bundle { 55 // to decode 56 val ren = Vec(DecodeWidth, Input(Bool())) 57 val raddr = Vec(DecodeWidth, Input(UInt(MemPredPCWidth.W))) // xor hashed decode pc(VaddrBits-1, 1) 58 // to rename 59 val rdata = Vec(RenameWidth, Output(new SSITEntry)) 60 // misc 61 val update = Input(new MemPredUpdateReq) // RegNext should be added outside 62 val csrCtrl = Input(new CustomCSRCtrlIO) 63 }) 64 65 // raddrs are sent to ssit in decode 66 // rdata will be send to rename 67 require(DecodeWidth == RenameWidth) 68 69 // data sram read port allocate 70 // 71 // SSIT update logic will reuse decode ssit read port. 72 // If io.update.valid, a redirect will be send to frontend, 73 // then decode will not need to read SSIT 74 val SSIT_DECODE_READ_PORT_BASE = 0 75 val SSIT_UPDATE_LOAD_READ_PORT = 0 76 val SSIT_UPDATE_STORE_READ_PORT = 1 77 val SSIT_READ_PORT_NUM = DecodeWidth 78 79 // data sram write port allocate 80 // load update and flush uses the same write port 81 val SSIT_MISC_WRITE_PORT = 0 82 val SSIT_UPDATE_LOAD_WRITE_PORT = 0 83 val SSIT_UPDATE_STORE_WRITE_PORT = 1 84 val SSIT_WRITE_PORT_NUM = 2 85 86 private def hasRen: Boolean = true 87 val valid_array = Module(new SyncDataModuleTemplate( 88 Bool(), 89 SSITSize, 90 SSIT_READ_PORT_NUM, 91 SSIT_WRITE_PORT_NUM, 92 hasRen = hasRen, 93 )) 94 95 val data_array = Module(new SyncDataModuleTemplate( 96 new SSITDataEntry, 97 SSITSize, 98 SSIT_READ_PORT_NUM, 99 SSIT_WRITE_PORT_NUM, 100 hasRen = hasRen, 101 )) 102 103 // TODO: use SRAM or not? 104 (0 until SSIT_WRITE_PORT_NUM).map(i => { 105 valid_array.io.wen(i) := false.B 106 valid_array.io.waddr(i) := 0.U 107 valid_array.io.wdata(i) := false.B 108 data_array.io.wen(i) := false.B 109 data_array.io.waddr(i) := 0.U 110 data_array.io.wdata(i) := 0.U.asTypeOf(new SSITDataEntry) 111 }) 112 113 val debug_valid = RegInit(VecInit(Seq.fill(SSITSize)(false.B))) 114 val debug_ssid = Reg(Vec(SSITSize, UInt(SSIDWidth.W))) 115 val debug_strict = Reg(Vec(SSITSize, Bool())) 116 if(!env.FPGAPlatform){ 117 dontTouch(debug_valid) 118 dontTouch(debug_ssid) 119 dontTouch(debug_strict) 120 } 121 122 val resetCounter = RegInit(0.U(ResetTimeMax2Pow.W)) 123 resetCounter := resetCounter + 1.U 124 125 for (i <- 0 until DecodeWidth) { 126 // io.rdata(i).valid := RegNext(valid(io.raddr(i))) 127 // io.rdata(i).ssid := RegNext(ssid(io.raddr(i))) 128 // io.rdata(i).strict := RegNext(strict(io.raddr(i)) && valid(io.raddr(i))) 129 130 // read SSIT in decode stage 131 valid_array.io.ren.get(i) := io.ren(i) 132 data_array.io.ren.get(i) := io.ren(i) 133 valid_array.io.raddr(i) := io.raddr(i) 134 data_array.io.raddr(i) := io.raddr(i) 135 136 // gen result in rename stage 137 io.rdata(i).valid := valid_array.io.rdata(i) 138 io.rdata(i).ssid := data_array.io.rdata(i).ssid 139 io.rdata(i).strict := data_array.io.rdata(i).strict 140 } 141 142 // flush SSIT 143 // reset period: ResetTimeMax2Pow 144 val resetStepCounter = RegInit(0.U(log2Up(SSITSize + 1).W)) 145 val s_idle :: s_flush :: Nil = Enum(2) 146 val state = RegInit(s_flush) 147 148 switch (state) { 149 is(s_idle) { 150 when(resetCounter(ResetTimeMax2Pow - 1, ResetTimeMin2Pow)(RegNext(io.csrCtrl.lvpred_timeout))) { 151 state := s_flush 152 resetCounter := 0.U 153 } 154 } 155 is(s_flush) { 156 when(resetStepCounter === (SSITSize - 1).U) { 157 state := s_idle // reset finished 158 resetStepCounter := 0.U 159 }.otherwise{ 160 resetStepCounter := resetStepCounter + 1.U 161 } 162 valid_array.io.wen(SSIT_MISC_WRITE_PORT) := true.B 163 valid_array.io.waddr(SSIT_MISC_WRITE_PORT) := resetStepCounter 164 valid_array.io.wdata(SSIT_MISC_WRITE_PORT) := false.B 165 debug_valid(resetStepCounter) := false.B 166 } 167 } 168 XSPerfAccumulate("reset_timeout", state === s_flush && resetCounter === 0.U) 169 170 // update SSIT if load violation redirect is detected 171 172 // update stage 0: read ssit 173 val s1_mempred_update_req_valid = RegNext(io.update.valid) 174 val s1_mempred_update_req = RegEnable(io.update, io.update.valid) 175 176 // when io.update.valid, take over ssit read port 177 when (io.update.valid) { 178 valid_array.io.raddr(SSIT_UPDATE_LOAD_READ_PORT) := io.update.ldpc 179 valid_array.io.raddr(SSIT_UPDATE_STORE_READ_PORT) := io.update.stpc 180 data_array.io.raddr(SSIT_UPDATE_LOAD_READ_PORT) := io.update.ldpc 181 data_array.io.raddr(SSIT_UPDATE_STORE_READ_PORT) := io.update.stpc 182 } 183 184 // update stage 1: get ssit read result 185 186 // Read result 187 // load has already been assigned with a store set 188 val s1_loadAssigned = valid_array.io.rdata(SSIT_UPDATE_LOAD_READ_PORT) 189 val s1_loadOldSSID = data_array.io.rdata(SSIT_UPDATE_LOAD_READ_PORT).ssid 190 val s1_loadStrict = data_array.io.rdata(SSIT_UPDATE_LOAD_READ_PORT).strict 191 // store has already been assigned with a store set 192 val s1_storeAssigned = valid_array.io.rdata(SSIT_UPDATE_STORE_READ_PORT) 193 val s1_storeOldSSID = data_array.io.rdata(SSIT_UPDATE_STORE_READ_PORT).ssid 194 val s1_storeStrict = data_array.io.rdata(SSIT_UPDATE_STORE_READ_PORT).strict 195 // val s1_ssidIsSame = s1_loadOldSSID === s1_storeOldSSID 196 197 // update stage 2, update ssit data_array 198 val s2_mempred_update_req_valid = RegNext(s1_mempred_update_req_valid) 199 val s2_mempred_update_req = RegEnable(s1_mempred_update_req, s1_mempred_update_req_valid) 200 val s2_loadAssigned = RegEnable(s1_loadAssigned, s1_mempred_update_req_valid) 201 val s2_storeAssigned = RegEnable(s1_storeAssigned, s1_mempred_update_req_valid) 202 val s2_loadOldSSID = RegEnable(s1_loadOldSSID, s1_mempred_update_req_valid) 203 val s2_storeOldSSID = RegEnable(s1_storeOldSSID, s1_mempred_update_req_valid) 204 val s2_loadStrict = RegEnable(s1_loadStrict, s1_mempred_update_req_valid) 205 206 val s2_ssidIsSame = s2_loadOldSSID === s2_storeOldSSID 207 // for now we just use lowest bits of ldpc as store set id 208 val s2_ldSsidAllocate = XORFold(s2_mempred_update_req.ldpc, SSIDWidth) 209 val s2_stSsidAllocate = XORFold(s2_mempred_update_req.stpc, SSIDWidth) 210 val s2_allocSsid = Mux(s2_ldSsidAllocate < s2_stSsidAllocate, s2_ldSsidAllocate, s2_stSsidAllocate) 211 // both the load and the store have already been assigned store sets 212 // but load's store set ID is smaller 213 val s2_winnerSSID = Mux(s2_loadOldSSID < s2_storeOldSSID, s2_loadOldSSID, s2_storeOldSSID) 214 215 def update_ld_ssit_entry(pc: UInt, valid: Bool, ssid: UInt, strict: Bool) = { 216 valid_array.io.wen(SSIT_UPDATE_LOAD_WRITE_PORT) := true.B 217 valid_array.io.waddr(SSIT_UPDATE_LOAD_WRITE_PORT) := pc 218 valid_array.io.wdata(SSIT_UPDATE_LOAD_WRITE_PORT) := valid 219 data_array.io.wen(SSIT_UPDATE_LOAD_WRITE_PORT) := true.B 220 data_array.io.waddr(SSIT_UPDATE_LOAD_WRITE_PORT) := pc 221 data_array.io.wdata(SSIT_UPDATE_LOAD_WRITE_PORT).ssid := ssid 222 data_array.io.wdata(SSIT_UPDATE_LOAD_WRITE_PORT).strict := strict 223 debug_valid(pc) := valid 224 debug_ssid(pc) := ssid 225 debug_strict(pc) := strict 226 } 227 228 def update_st_ssit_entry(pc: UInt, valid: Bool, ssid: UInt, strict: Bool) = { 229 valid_array.io.wen(SSIT_UPDATE_STORE_WRITE_PORT) := true.B 230 valid_array.io.waddr(SSIT_UPDATE_STORE_WRITE_PORT) := pc 231 valid_array.io.wdata(SSIT_UPDATE_STORE_WRITE_PORT):= valid 232 data_array.io.wen(SSIT_UPDATE_STORE_WRITE_PORT) := true.B 233 data_array.io.waddr(SSIT_UPDATE_STORE_WRITE_PORT) := pc 234 data_array.io.wdata(SSIT_UPDATE_STORE_WRITE_PORT).ssid := ssid 235 data_array.io.wdata(SSIT_UPDATE_STORE_WRITE_PORT).strict := strict 236 debug_valid(pc) := valid 237 debug_ssid(pc) := ssid 238 debug_strict(pc) := strict 239 } 240 241 when(s2_mempred_update_req_valid){ 242 switch (Cat(s2_loadAssigned, s2_storeAssigned)) { 243 // 1. "If neither the load nor the store has been assigned a store set, 244 // two are allocated and assigned to each instruction." 245 is ("b00".U(2.W)) { 246 update_ld_ssit_entry( 247 pc = s2_mempred_update_req.ldpc, 248 valid = true.B, 249 ssid = s2_allocSsid, 250 strict = false.B 251 ) 252 update_st_ssit_entry( 253 pc = s2_mempred_update_req.stpc, 254 valid = true.B, 255 ssid = s2_allocSsid, 256 strict = false.B 257 ) 258 } 259 // 2. "If the load has been assigned a store set, but the store has not, 260 // one is allocated and assigned to the store instructions." 261 is ("b10".U(2.W)) { 262 update_st_ssit_entry( 263 pc = s2_mempred_update_req.stpc, 264 valid = true.B, 265 ssid = s2_ldSsidAllocate, 266 strict = false.B 267 ) 268 } 269 // 3. "If the store has been assigned a store set, but the load has not, 270 // one is allocated and assigned to the load instructions." 271 is ("b01".U(2.W)) { 272 update_ld_ssit_entry( 273 pc = s2_mempred_update_req.ldpc, 274 valid = true.B, 275 ssid = s2_stSsidAllocate, 276 strict = false.B 277 ) 278 } 279 // 4. "If both the load and the store have already been assigned store sets, 280 // one of the two store sets is declared the "winner". 281 // The instruction belonging to the loser’s store set is assigned the winner’s store set." 282 is ("b11".U(2.W)) { 283 update_ld_ssit_entry( 284 pc = s2_mempred_update_req.ldpc, 285 valid = true.B, 286 ssid = s2_winnerSSID, 287 strict = false.B 288 ) 289 update_st_ssit_entry( 290 pc = s2_mempred_update_req.stpc, 291 valid = true.B, 292 ssid = s2_winnerSSID, 293 strict = false.B 294 ) 295 when(s2_ssidIsSame){ 296 data_array.io.wdata(SSIT_UPDATE_LOAD_READ_PORT).strict := true.B 297 debug_strict(s2_mempred_update_req.ldpc) := true.B 298 } 299 } 300 } 301 } 302 303 // make SyncDataModuleTemplate happy 304 when(valid_array.io.waddr(SSIT_UPDATE_LOAD_WRITE_PORT) === valid_array.io.waddr(SSIT_UPDATE_STORE_WRITE_PORT)){ 305 valid_array.io.wen(SSIT_UPDATE_STORE_WRITE_PORT) := false.B 306 } 307 308 when(data_array.io.waddr(SSIT_UPDATE_LOAD_WRITE_PORT) === data_array.io.waddr(SSIT_UPDATE_STORE_WRITE_PORT)){ 309 data_array.io.wen(SSIT_UPDATE_STORE_WRITE_PORT) := false.B 310 } 311 312 XSPerfAccumulate("ssit_update_lxsx", s2_mempred_update_req_valid && !s2_loadAssigned && !s2_storeAssigned) 313 XSPerfAccumulate("ssit_update_lysx", s2_mempred_update_req_valid && s2_loadAssigned && !s2_storeAssigned) 314 XSPerfAccumulate("ssit_update_lxsy", s2_mempred_update_req_valid && !s2_loadAssigned && s2_storeAssigned) 315 XSPerfAccumulate("ssit_update_lysy", s2_mempred_update_req_valid && s2_loadAssigned && s2_storeAssigned) 316 XSPerfAccumulate("ssit_update_should_strict", s2_mempred_update_req_valid && s2_ssidIsSame && s2_loadAssigned && s2_storeAssigned) 317 XSPerfAccumulate("ssit_update_strict_failed", 318 s2_mempred_update_req_valid && s2_ssidIsSame && s2_loadStrict && s2_loadAssigned && s2_storeAssigned 319 ) // should be zero 320 321 // debug 322 XSDebug(s2_mempred_update_req.valid, "%d: SSIT update: load pc %x store pc %x\n", GTimer(), s2_mempred_update_req.ldpc, s2_mempred_update_req.stpc) 323 XSDebug(s2_mempred_update_req.valid, "%d: SSIT update: load valid %b ssid %x store valid %b ssid %x\n", GTimer(), s2_loadAssigned, s2_loadOldSSID, s2_storeAssigned, s2_storeOldSSID) 324} 325 326 327// Last Fetched Store Table Entry 328class LFSTEntry(implicit p: Parameters) extends XSBundle { 329 val valid = Bool() 330 val robIdx = new RobPtr 331} 332 333class LFSTReq(implicit p: Parameters) extends XSBundle { 334 val isstore = Bool() 335 val ssid = UInt(SSIDWidth.W) // use ssid to lookup LFST 336 val robIdx = new RobPtr 337} 338 339class LFSTResp(implicit p: Parameters) extends XSBundle { 340 val shouldWait = Bool() 341 val robIdx = new RobPtr 342} 343 344class DispatchLFSTIO(implicit p: Parameters) extends XSBundle { 345 val req = Vec(RenameWidth, Valid(new LFSTReq)) 346 val resp = Vec(RenameWidth, Flipped(Valid(new LFSTResp))) 347} 348 349// Last Fetched Store Table 350class LFST(implicit p: Parameters) extends XSModule { 351 val io = IO(new Bundle { 352 // when redirect, mark canceled store as invalid 353 val redirect = Input(Valid(new Redirect)) 354 val dispatch = Flipped(new DispatchLFSTIO) 355 // when store issued, mark store as invalid 356 val storeIssue = Vec(backendParams.StaExuCnt, Flipped(Valid(new DynInst))) 357 val csrCtrl = Input(new CustomCSRCtrlIO) 358 }) 359 360 val validVec = RegInit(VecInit(Seq.fill(LFSTSize)(VecInit(Seq.fill(LFSTWidth)(false.B))))) 361 val robIdxVec = Reg(Vec(LFSTSize, Vec(LFSTWidth, new RobPtr))) 362 val allocPtr = RegInit(VecInit(Seq.fill(LFSTSize)(0.U(log2Up(LFSTWidth).W)))) 363 val valid = Wire(Vec(LFSTSize, Bool())) 364 (0 until LFSTSize).map(i => { 365 valid(i) := validVec(i).asUInt.orR 366 }) 367 368 // read LFST in rename stage 369 for (i <- 0 until RenameWidth) { 370 io.dispatch.resp(i).valid := io.dispatch.req(i).valid 371 372 // If store-load pair is in the same dispatch bundle, loadWaitBit should also be set for load 373 val hitInDispatchBundleVec = if(i > 0){ 374 WireInit(VecInit((0 until i).map(j => 375 io.dispatch.req(j).valid && 376 io.dispatch.req(j).bits.isstore && 377 io.dispatch.req(j).bits.ssid === io.dispatch.req(i).bits.ssid 378 ))) 379 } else { 380 WireInit(VecInit(Seq(false.B))) // DontCare 381 } 382 val hitInDispatchBundle = hitInDispatchBundleVec.asUInt.orR 383 // Check if store set is valid in LFST 384 io.dispatch.resp(i).bits.shouldWait := ( 385 (valid(io.dispatch.req(i).bits.ssid) || hitInDispatchBundle) && 386 io.dispatch.req(i).valid && 387 (!io.dispatch.req(i).bits.isstore || io.csrCtrl.storeset_wait_store) 388 ) && !io.csrCtrl.lvpred_disable || io.csrCtrl.no_spec_load 389 io.dispatch.resp(i).bits.robIdx := robIdxVec(io.dispatch.req(i).bits.ssid)(allocPtr(io.dispatch.req(i).bits.ssid)-1.U) 390 if(i > 0){ 391 (0 until i).map(j => 392 when(hitInDispatchBundleVec(j)){ 393 io.dispatch.resp(i).bits.robIdx := io.dispatch.req(j).bits.robIdx 394 } 395 ) 396 } 397 } 398 399 // when store is issued, mark it as invalid 400 (0 until backendParams.StaExuCnt).map(i => { 401 // TODO: opt timing 402 (0 until LFSTWidth).map(j => { 403 when(io.storeIssue(i).valid && io.storeIssue(i).bits.storeSetHit && io.storeIssue(i).bits.robIdx.value === robIdxVec(io.storeIssue(i).bits.ssid)(j).value){ 404 validVec(io.storeIssue(i).bits.ssid)(j) := false.B 405 } 406 }) 407 }) 408 409 // when store is dispatched, mark it as valid 410 (0 until RenameWidth).map(i => { 411 when(io.dispatch.req(i).valid && io.dispatch.req(i).bits.isstore){ 412 val waddr = io.dispatch.req(i).bits.ssid 413 val wptr = allocPtr(waddr) 414 allocPtr(waddr) := allocPtr(waddr) + 1.U 415 validVec(waddr)(wptr) := true.B 416 robIdxVec(waddr)(wptr) := io.dispatch.req(i).bits.robIdx 417 } 418 }) 419 420 // when redirect, cancel store influenced 421 (0 until LFSTSize).map(i => { 422 (0 until LFSTWidth).map(j => { 423 when(validVec(i)(j) && robIdxVec(i)(j).needFlush(io.redirect)){ 424 validVec(i)(j) := false.B 425 } 426 }) 427 }) 428 429 // recover robIdx after squash 430 // behavior model, to be refactored later 431 when(RegNext(io.redirect.fire)) { 432 (0 until LFSTSize).map(i => { 433 (0 until LFSTWidth).map(j => { 434 val check_position = WireInit(allocPtr(i) + (j+1).U) 435 when(!validVec(i)(check_position)){ 436 allocPtr(i) := check_position 437 } 438 }) 439 }) 440 } 441} 442