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