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] Kevin Skadron, Pritpal S. Ahuja, Margaret Martonosi, and Douglas W. Clark. "[Improving prediction for procedure 22* returns with return-address-stack repair mechanisms.](https://doi.org/10.1109/MICRO.1998.742787)" 31st Annual 23* ACM/IEEE International Symposium on Microarchitecture (MICRO). 1998. 24* [2] Tan Hongze, and Wang Jian. "[A Return Address Predictor Based on Persistent Stack.] 25* (https://crad.ict.ac.cn/en/article/doi/10.7544/issn1000-1239.202111274)" Journal of Computer Research and Development 26* (CRAD) 60.6: 1337-1345. 2023. 27***************************************************************************************/ 28package xiangshan.frontend 29 30import chisel3._ 31import chisel3.util._ 32import org.chipsalliance.cde.config.Parameters 33import utility._ 34import utils._ 35import xiangshan._ 36import xiangshan.frontend._ 37 38class RASEntry()(implicit p: Parameters) extends XSBundle { 39 val retAddr = UInt(VAddrBits.W) 40 val ctr = UInt(RasCtrSize.W) // layer of nested call functions 41 def =/=(that: RASEntry) = this.retAddr =/= that.retAddr || this.ctr =/= that.ctr 42} 43 44class RASPtr(implicit p: Parameters) extends CircularQueuePtr[RASPtr](p => p(XSCoreParamsKey).RasSpecSize) {} 45 46object RASPtr { 47 def apply(f: Bool, v: UInt)(implicit p: Parameters): RASPtr = { 48 val ptr = Wire(new RASPtr) 49 ptr.flag := f 50 ptr.value := v 51 ptr 52 } 53 def inverse(ptr: RASPtr)(implicit p: Parameters): RASPtr = 54 apply(!ptr.flag, ptr.value) 55} 56 57class RASInternalMeta(implicit p: Parameters) extends XSBundle { 58 val ssp = UInt(log2Up(RasSize).W) 59 val sctr = UInt(RasCtrSize.W) 60 val TOSW = new RASPtr 61 val TOSR = new RASPtr 62 val NOS = new RASPtr 63} 64 65object RASInternalMeta { 66 def apply(ssp: UInt, sctr: UInt, TOSW: RASPtr, TOSR: RASPtr, NOS: RASPtr)(implicit p: Parameters): RASInternalMeta = { 67 val e = Wire(new RASInternalMeta) 68 e.ssp := ssp 69 e.TOSW := TOSW 70 e.TOSR := TOSR 71 e.NOS := NOS 72 e 73 } 74} 75 76class RASMeta(implicit p: Parameters) extends XSBundle { 77 val ssp = UInt(log2Up(RasSize).W) 78 val TOSW = new RASPtr 79} 80 81object RASMeta { 82 def apply(ssp: UInt, sctr: UInt, TOSW: RASPtr, TOSR: RASPtr, NOS: RASPtr)(implicit p: Parameters): RASMeta = { 83 val e = Wire(new RASMeta) 84 e.ssp := ssp 85 e.TOSW := TOSW 86 e 87 } 88} 89 90class RASDebug(implicit p: Parameters) extends XSBundle { 91 val spec_queue = Output(Vec(RasSpecSize, new RASEntry)) 92 val spec_nos = Output(Vec(RasSpecSize, new RASPtr)) 93 val commit_stack = Output(Vec(RasSize, new RASEntry)) 94} 95 96class RAS(implicit p: Parameters) extends BasePredictor with HasCircularQueuePtrHelper { 97 override val meta_size = WireInit(0.U.asTypeOf(new RASMeta)).getWidth 98 99 object RASEntry { 100 def apply(retAddr: UInt, ctr: UInt): RASEntry = { 101 val e = Wire(new RASEntry) 102 e.retAddr := retAddr 103 e.ctr := ctr 104 e 105 } 106 } 107 108 class RASStack(rasSize: Int, rasSpecSize: Int) extends XSModule with HasCircularQueuePtrHelper { 109 val io = IO(new Bundle { 110 val spec_push_valid = Input(Bool()) 111 val spec_pop_valid = Input(Bool()) 112 val spec_push_addr = Input(UInt(VAddrBits.W)) 113 // for write bypass between s2 and s3 114 115 val s2_fire = Input(Bool()) 116 val s3_fire = Input(Bool()) 117 val s3_cancel = Input(Bool()) 118 val s3_meta = Input(new RASInternalMeta) 119 val s3_missed_pop = Input(Bool()) 120 val s3_missed_push = Input(Bool()) 121 val s3_pushAddr = Input(UInt(VAddrBits.W)) 122 val spec_pop_addr = Output(UInt(VAddrBits.W)) 123 124 val commit_valid = Input(Bool()) 125 val commit_push_valid = Input(Bool()) 126 val commit_pop_valid = Input(Bool()) 127 val commit_push_addr = Input(UInt(VAddrBits.W)) 128 val commit_meta_TOSW = Input(new RASPtr) 129 // for debug purpose only 130 val commit_meta_ssp = Input(UInt(log2Up(RasSize).W)) 131 132 val redirect_valid = Input(Bool()) 133 val redirect_isCall = Input(Bool()) 134 val redirect_isRet = Input(Bool()) 135 val redirect_meta_ssp = Input(UInt(log2Up(RasSize).W)) 136 val redirect_meta_sctr = Input(UInt(RasCtrSize.W)) 137 val redirect_meta_TOSW = Input(new RASPtr) 138 val redirect_meta_TOSR = Input(new RASPtr) 139 val redirect_meta_NOS = Input(new RASPtr) 140 val redirect_callAddr = Input(UInt(VAddrBits.W)) 141 142 val ssp = Output(UInt(log2Up(RasSize).W)) 143 val sctr = Output(UInt(RasCtrSize.W)) 144 val nsp = Output(UInt(log2Up(RasSize).W)) 145 val TOSR = Output(new RASPtr) 146 val TOSW = Output(new RASPtr) 147 val NOS = Output(new RASPtr) 148 val BOS = Output(new RASPtr) 149 150 val spec_near_overflow = Output(Bool()) 151 152 val debug = new RASDebug 153 }) 154 155 val commit_stack = RegInit(VecInit(Seq.fill(RasSize)(RASEntry(0.U, 0.U)))) 156 val spec_queue = RegInit(VecInit(Seq.fill(rasSpecSize)(RASEntry(0.U, 0.U)))) 157 val spec_nos = RegInit(VecInit(Seq.fill(rasSpecSize)(RASPtr(false.B, 0.U)))) 158 159 val nsp = RegInit(0.U(log2Up(rasSize).W)) 160 val ssp = RegInit(0.U(log2Up(rasSize).W)) 161 162 val sctr = RegInit(0.U(RasCtrSize.W)) 163 val TOSR = RegInit(RASPtr(true.B, (RasSpecSize - 1).U)) 164 val TOSW = RegInit(RASPtr(false.B, 0.U)) 165 val BOS = RegInit(RASPtr(false.B, 0.U)) 166 167 val spec_near_overflowed = RegInit(false.B) 168 169 val writeBypassEntry = Reg(new RASEntry) 170 val writeBypassNos = Reg(new RASPtr) 171 172 val writeBypassValid = RegInit(0.B) 173 val writeBypassValidWire = Wire(Bool()) 174 175 def TOSRinRange(currentTOSR: RASPtr, currentTOSW: RASPtr) = { 176 val inflightValid = WireInit(false.B) 177 // if in range, TOSR should be no younger than BOS and strictly younger than TOSW 178 when(!isBefore(currentTOSR, BOS) && isBefore(currentTOSR, currentTOSW)) { 179 inflightValid := true.B 180 } 181 inflightValid 182 } 183 184 def getCommitTop(currentSsp: UInt) = 185 commit_stack(currentSsp) 186 187 def getTopNos(currentTOSR: RASPtr, allowBypass: Boolean): RASPtr = { 188 val ret = Wire(new RASPtr) 189 if (allowBypass) { 190 when(writeBypassValid) { 191 ret := writeBypassNos 192 }.otherwise { 193 ret := spec_nos(TOSR.value) 194 } 195 } else { 196 ret := spec_nos(TOSR.value) // invalid when TOSR is not in range 197 } 198 ret 199 } 200 201 def getTop( 202 currentSsp: UInt, 203 currentSctr: UInt, 204 currentTOSR: RASPtr, 205 currentTOSW: RASPtr, 206 allowBypass: Boolean 207 ): RASEntry = { 208 val ret = Wire(new RASEntry) 209 if (allowBypass) { 210 when(writeBypassValid) { 211 ret := writeBypassEntry 212 }.elsewhen(TOSRinRange(currentTOSR, currentTOSW)) { 213 ret := spec_queue(currentTOSR.value) 214 }.otherwise { 215 ret := getCommitTop(currentSsp) 216 } 217 } else { 218 when(TOSRinRange(currentTOSR, currentTOSW)) { 219 ret := spec_queue(currentTOSR.value) 220 }.otherwise { 221 ret := getCommitTop(currentSsp) 222 } 223 } 224 225 ret 226 } 227 228 // it would be unsafe for specPtr manipulation if specSize is not power of 2 229 assert(log2Up(RasSpecSize) == log2Floor(RasSpecSize)) 230 def ctrMax = ((1L << RasCtrSize) - 1).U 231 def ptrInc(ptr: UInt) = ptr + 1.U 232 def ptrDec(ptr: UInt) = ptr - 1.U 233 234 def specPtrInc(ptr: RASPtr) = ptr + 1.U 235 def specPtrDec(ptr: RASPtr) = ptr - 1.U 236 237 when(io.redirect_valid && io.redirect_isCall) { 238 writeBypassValidWire := true.B 239 writeBypassValid := true.B 240 }.elsewhen(io.redirect_valid) { 241 // clear current top writeBypass if doing redirect 242 writeBypassValidWire := false.B 243 writeBypassValid := false.B 244 }.elsewhen(io.s2_fire) { 245 writeBypassValidWire := io.spec_push_valid 246 writeBypassValid := io.spec_push_valid 247 }.elsewhen(io.s3_fire) { 248 writeBypassValidWire := false.B 249 writeBypassValid := false.B 250 }.otherwise { 251 writeBypassValidWire := writeBypassValid 252 } 253 254 val topEntry = getTop(ssp, sctr, TOSR, TOSW, true) 255 val topNos = getTopNos(TOSR, true) 256 val redirectTopEntry = 257 getTop(io.redirect_meta_ssp, io.redirect_meta_sctr, io.redirect_meta_TOSR, io.redirect_meta_TOSW, false) 258 val redirectTopNos = io.redirect_meta_NOS 259 val s3TopEntry = getTop(io.s3_meta.ssp, io.s3_meta.sctr, io.s3_meta.TOSR, io.s3_meta.TOSW, false) 260 val s3TopNos = io.s3_meta.NOS 261 262 val writeEntry = Wire(new RASEntry) 263 val writeNos = Wire(new RASPtr) 264 writeEntry.retAddr := Mux(io.redirect_valid && io.redirect_isCall, io.redirect_callAddr, io.spec_push_addr) 265 writeEntry.ctr := Mux( 266 io.redirect_valid && io.redirect_isCall, 267 Mux( 268 redirectTopEntry.retAddr === io.redirect_callAddr && redirectTopEntry.ctr < ctrMax, 269 io.redirect_meta_sctr + 1.U, 270 0.U 271 ), 272 Mux(topEntry.retAddr === io.spec_push_addr && topEntry.ctr < ctrMax, sctr + 1.U, 0.U) 273 ) 274 275 writeNos := Mux(io.redirect_valid && io.redirect_isCall, io.redirect_meta_TOSR, TOSR) 276 277 when(io.spec_push_valid || (io.redirect_valid && io.redirect_isCall)) { 278 writeBypassEntry := writeEntry 279 writeBypassNos := writeNos 280 } 281 282 val realPush = Wire(Bool()) 283 val realWriteEntry = Wire(new RASEntry) 284 val timingTop = RegInit(0.U.asTypeOf(new RASEntry)) 285 val timingNos = RegInit(0.U.asTypeOf(new RASPtr)) 286 287 when(writeBypassValidWire) { 288 when((io.redirect_valid && io.redirect_isCall) || io.spec_push_valid) { 289 timingTop := writeEntry 290 timingNos := writeNos 291 }.otherwise { 292 timingTop := writeBypassEntry 293 timingNos := writeBypassNos 294 } 295 296 }.elsewhen(io.redirect_valid && io.redirect_isRet) { 297 // getTop using redirect Nos as TOSR 298 val popRedSsp = Wire(UInt(log2Up(rasSize).W)) 299 val popRedSctr = Wire(UInt(RasCtrSize.W)) 300 val popRedTOSR = io.redirect_meta_NOS 301 val popRedTOSW = io.redirect_meta_TOSW 302 303 when(io.redirect_meta_sctr > 0.U) { 304 popRedSctr := io.redirect_meta_sctr - 1.U 305 popRedSsp := io.redirect_meta_ssp 306 }.elsewhen(TOSRinRange(popRedTOSR, TOSW)) { 307 popRedSsp := ptrDec(io.redirect_meta_ssp) 308 popRedSctr := spec_queue(popRedTOSR.value).ctr 309 }.otherwise { 310 popRedSsp := ptrDec(io.redirect_meta_ssp) 311 popRedSctr := getCommitTop(ptrDec(io.redirect_meta_ssp)).ctr 312 } 313 // We are deciding top for the next cycle, no need to use bypass here 314 timingTop := getTop(popRedSsp, popRedSctr, popRedTOSR, popRedTOSW, false) 315 }.elsewhen(io.redirect_valid) { 316 // Neither call nor ret 317 val popSsp = io.redirect_meta_ssp 318 val popSctr = io.redirect_meta_sctr 319 val popTOSR = io.redirect_meta_TOSR 320 val popTOSW = io.redirect_meta_TOSW 321 322 timingTop := getTop(popSsp, popSctr, popTOSR, popTOSW, false) 323 324 }.elsewhen(io.spec_pop_valid) { 325 // getTop using current Nos as TOSR 326 val popSsp = Wire(UInt(log2Up(rasSize).W)) 327 val popSctr = Wire(UInt(RasCtrSize.W)) 328 val popTOSR = topNos 329 val popTOSW = TOSW 330 331 when(sctr > 0.U) { 332 popSctr := sctr - 1.U 333 popSsp := ssp 334 }.elsewhen(TOSRinRange(popTOSR, TOSW)) { 335 popSsp := ptrDec(ssp) 336 popSctr := spec_queue(popTOSR.value).ctr 337 }.otherwise { 338 popSsp := ptrDec(ssp) 339 popSctr := getCommitTop(ptrDec(ssp)).ctr 340 } 341 // We are deciding top for the next cycle, no need to use bypass here 342 timingTop := getTop(popSsp, popSctr, popTOSR, popTOSW, false) 343 }.elsewhen(realPush) { 344 // just updating spec queue, cannot read from there 345 timingTop := realWriteEntry 346 }.elsewhen(io.s3_cancel) { 347 // s3 is different with s2 348 timingTop := getTop(io.s3_meta.ssp, io.s3_meta.sctr, io.s3_meta.TOSR, io.s3_meta.TOSW, false) 349 when(io.s3_missed_push) { 350 val writeEntry_s3 = Wire(new RASEntry) 351 timingTop := writeEntry_s3 352 writeEntry_s3.retAddr := io.s3_pushAddr 353 writeEntry_s3.ctr := Mux( 354 timingTop.retAddr === io.s3_pushAddr && io.s3_meta.sctr < ctrMax, 355 io.s3_meta.sctr + 1.U, 356 0.U 357 ) 358 }.elsewhen(io.s3_missed_pop) { 359 val popRedSsp_s3 = Wire(UInt(log2Up(rasSize).W)) 360 val popRedSctr_s3 = Wire(UInt(RasCtrSize.W)) 361 val popRedTOSR_s3 = io.s3_meta.NOS 362 val popRedTOSW_s3 = io.s3_meta.TOSW 363 364 when(io.s3_meta.sctr > 0.U) { 365 popRedSctr_s3 := io.s3_meta.sctr - 1.U 366 popRedSsp_s3 := io.s3_meta.ssp 367 }.elsewhen(TOSRinRange(popRedTOSR_s3, popRedTOSW_s3)) { 368 popRedSsp_s3 := ptrDec(io.s3_meta.ssp) 369 popRedSctr_s3 := spec_queue(popRedTOSR_s3.value).ctr 370 }.otherwise { 371 popRedSsp_s3 := ptrDec(io.s3_meta.ssp) 372 popRedSctr_s3 := getCommitTop(ptrDec(io.s3_meta.ssp)).ctr 373 } 374 // We are deciding top for the next cycle, no need to use bypass here 375 timingTop := getTop(popRedSsp_s3, popRedSctr_s3, popRedTOSR_s3, popRedTOSW_s3, false) 376 } 377 }.otherwise { 378 // easy case 379 val popSsp = ssp 380 val popSctr = sctr 381 val popTOSR = TOSR 382 val popTOSW = TOSW 383 timingTop := getTop(popSsp, popSctr, popTOSR, popTOSW, false) 384 } 385 val diffTop = Mux(writeBypassValid, writeBypassEntry.retAddr, topEntry.retAddr) 386 387 XSPerfAccumulate("ras_top_mismatch", diffTop =/= timingTop.retAddr); 388 // could diff when more pop than push and a commit stack is updated with inflight info 389 390 val realWriteEntry_next = RegEnable(writeEntry, io.s2_fire || io.redirect_isCall) 391 val s3_missPushEntry = Wire(new RASEntry) 392 val s3_missPushAddr = Wire(new RASPtr) 393 val s3_missPushNos = Wire(new RASPtr) 394 395 s3_missPushEntry.retAddr := io.s3_pushAddr 396 s3_missPushEntry.ctr := Mux( 397 s3TopEntry.retAddr === io.s3_pushAddr && s3TopEntry.ctr < ctrMax, 398 io.s3_meta.sctr + 1.U, 399 0.U 400 ) 401 s3_missPushAddr := io.s3_meta.TOSW 402 s3_missPushNos := io.s3_meta.TOSR 403 404 realWriteEntry := Mux( 405 io.redirect_isCall, 406 realWriteEntry_next, 407 Mux(io.s3_missed_push, s3_missPushEntry, realWriteEntry_next) 408 ) 409 410 val realWriteAddr_next = RegEnable( 411 Mux(io.redirect_valid && io.redirect_isCall, io.redirect_meta_TOSW, TOSW), 412 io.s2_fire || (io.redirect_valid && io.redirect_isCall) 413 ) 414 val realWriteAddr = 415 Mux(io.redirect_isCall, realWriteAddr_next, Mux(io.s3_missed_push, s3_missPushAddr, realWriteAddr_next)) 416 val realNos_next = RegEnable( 417 Mux(io.redirect_valid && io.redirect_isCall, io.redirect_meta_TOSR, TOSR), 418 io.s2_fire || (io.redirect_valid && io.redirect_isCall) 419 ) 420 val realNos = Mux(io.redirect_isCall, realNos_next, Mux(io.s3_missed_push, s3_missPushNos, realNos_next)) 421 422 realPush := (io.s3_fire && (!io.s3_cancel && RegEnable( 423 io.spec_push_valid, 424 io.s2_fire 425 ) || io.s3_missed_push)) || RegNext(io.redirect_valid && io.redirect_isCall) 426 427 when(realPush) { 428 spec_queue(realWriteAddr.value) := realWriteEntry 429 spec_nos(realWriteAddr.value) := realNos 430 } 431 432 def specPush( 433 retAddr: UInt, 434 currentSsp: UInt, 435 currentSctr: UInt, 436 currentTOSR: RASPtr, 437 currentTOSW: RASPtr, 438 topEntry: RASEntry 439 ) = { 440 TOSR := currentTOSW 441 TOSW := specPtrInc(currentTOSW) 442 // spec sp and ctr should always be maintained 443 when(topEntry.retAddr === retAddr && currentSctr < ctrMax) { 444 sctr := currentSctr + 1.U 445 }.otherwise { 446 ssp := ptrInc(currentSsp) 447 sctr := 0.U 448 } 449 } 450 451 when(io.spec_push_valid) { 452 specPush(io.spec_push_addr, ssp, sctr, TOSR, TOSW, topEntry) 453 } 454 def specPop( 455 currentSsp: UInt, 456 currentSctr: UInt, 457 currentTOSR: RASPtr, 458 currentTOSW: RASPtr, 459 currentTopNos: RASPtr 460 ) = { 461 // TOSR is only maintained when spec queue is not empty 462 when(TOSRinRange(currentTOSR, currentTOSW)) { 463 TOSR := currentTopNos 464 } 465 // spec sp and ctr should always be maintained 466 when(currentSctr > 0.U) { 467 sctr := currentSctr - 1.U 468 }.elsewhen(TOSRinRange(currentTopNos, currentTOSW)) { 469 // in range, use inflight data 470 ssp := ptrDec(currentSsp) 471 sctr := spec_queue(currentTopNos.value).ctr 472 }.otherwise { 473 // NOS not in range, use commit data 474 ssp := ptrDec(currentSsp) 475 sctr := getCommitTop(ptrDec(currentSsp)).ctr 476 // in overflow state, we cannot determine the next sctr, sctr here is not accurate 477 } 478 } 479 when(io.spec_pop_valid) { 480 specPop(ssp, sctr, TOSR, TOSW, topNos) 481 } 482 483 // io.spec_pop_addr := Mux(writeBypassValid, writeBypassEntry.retAddr, topEntry.retAddr) 484 485 io.spec_pop_addr := timingTop.retAddr 486 io.BOS := BOS 487 io.TOSW := TOSW 488 io.TOSR := TOSR 489 io.NOS := topNos 490 io.ssp := ssp 491 io.sctr := sctr 492 io.nsp := nsp 493 494 when(io.s3_cancel) { 495 // recovery of all related pointers 496 TOSR := io.s3_meta.TOSR 497 TOSW := io.s3_meta.TOSW 498 ssp := io.s3_meta.ssp 499 sctr := io.s3_meta.sctr 500 501 // for missing pop, we also need to do a pop here 502 when(io.s3_missed_pop) { 503 specPop(io.s3_meta.ssp, io.s3_meta.sctr, io.s3_meta.TOSR, io.s3_meta.TOSW, io.s3_meta.NOS) 504 } 505 when(io.s3_missed_push) { 506 // do not use any bypass from f2 507 specPush(io.s3_pushAddr, io.s3_meta.ssp, io.s3_meta.sctr, io.s3_meta.TOSR, io.s3_meta.TOSW, s3TopEntry) 508 } 509 } 510 511 val commitTop = commit_stack(nsp) 512 513 when(io.commit_pop_valid) { 514 515 val nsp_update = Wire(UInt(log2Up(rasSize).W)) 516 when(io.commit_meta_ssp =/= nsp) { 517 // force set nsp to commit ssp to avoid permanent errors 518 nsp_update := io.commit_meta_ssp 519 }.otherwise { 520 nsp_update := nsp 521 } 522 523 // if ctr > 0, --ctr in stack, otherwise --nsp 524 when(commitTop.ctr > 0.U) { 525 commit_stack(nsp_update).ctr := commitTop.ctr - 1.U 526 nsp := nsp_update 527 }.otherwise { 528 nsp := ptrDec(nsp_update); 529 } 530 // XSError(io.commit_meta_ssp =/= nsp, "nsp mismatch with expected ssp") 531 } 532 533 val commit_push_addr = spec_queue(io.commit_meta_TOSW.value).retAddr 534 535 when(io.commit_push_valid) { 536 val nsp_update = Wire(UInt(log2Up(rasSize).W)) 537 when(io.commit_meta_ssp =/= nsp) { 538 // force set nsp to commit ssp to avoid permanent errors 539 nsp_update := io.commit_meta_ssp 540 }.otherwise { 541 nsp_update := nsp 542 } 543 // if ctr < max && topAddr == push addr, ++ctr, otherwise ++nsp 544 when(commitTop.ctr < ctrMax && commitTop.retAddr === commit_push_addr) { 545 commit_stack(nsp_update).ctr := commitTop.ctr + 1.U 546 nsp := nsp_update 547 }.otherwise { 548 nsp := ptrInc(nsp_update) 549 commit_stack(ptrInc(nsp_update)).retAddr := commit_push_addr 550 commit_stack(ptrInc(nsp_update)).ctr := 0.U 551 } 552 553 // XSError(io.commit_meta_ssp =/= nsp, "nsp mismatch with expected ssp") 554 // XSError(io.commit_push_addr =/= commit_push_addr, "addr from commit mismatch with addr from spec") 555 } 556 557 when(io.commit_push_valid) { 558 BOS := io.commit_meta_TOSW 559 }.elsewhen(io.commit_valid && (distanceBetween(io.commit_meta_TOSW, BOS) > 2.U)) { 560 BOS := specPtrDec(io.commit_meta_TOSW) 561 } 562 XSError( 563 io.commit_valid && (distanceBetween(io.commit_meta_TOSW, BOS) > 2.U), 564 "The use of inference queue of the RAS module has unexpected situations" 565 ) 566 567 when(io.redirect_valid) { 568 TOSR := io.redirect_meta_TOSR 569 TOSW := io.redirect_meta_TOSW 570 ssp := io.redirect_meta_ssp 571 sctr := io.redirect_meta_sctr 572 573 when(io.redirect_isCall) { 574 specPush( 575 io.redirect_callAddr, 576 io.redirect_meta_ssp, 577 io.redirect_meta_sctr, 578 io.redirect_meta_TOSR, 579 io.redirect_meta_TOSW, 580 redirectTopEntry 581 ) 582 } 583 when(io.redirect_isRet) { 584 specPop( 585 io.redirect_meta_ssp, 586 io.redirect_meta_sctr, 587 io.redirect_meta_TOSR, 588 io.redirect_meta_TOSW, 589 redirectTopNos 590 ) 591 } 592 } 593 594 when(distanceBetween(TOSW, BOS) > (rasSpecSize - 2).U) { 595 spec_near_overflowed := true.B 596 }.otherwise { 597 spec_near_overflowed := false.B 598 } 599 600 io.spec_near_overflow := spec_near_overflowed 601 XSPerfAccumulate("spec_near_overflow", spec_near_overflowed) 602 io.debug.commit_stack.zipWithIndex.foreach { case (a, i) => a := commit_stack(i) } 603 io.debug.spec_nos.zipWithIndex.foreach { case (a, i) => a := spec_nos(i) } 604 io.debug.spec_queue.zipWithIndex.foreach { case (a, i) => a := spec_queue(i) } 605 } 606 607 val stack = Module(new RASStack(RasSize, RasSpecSize)).io 608 609 val stack_near_overflow = stack.spec_near_overflow 610 val s2_spec_push = WireInit(false.B) 611 val s2_spec_pop = WireInit(false.B) 612 val s2_full_pred = io.in.bits.resp_in(0).s2.full_pred(2) 613 // when last inst is an rvi call, fall through address would be set to the middle of it, so an addition is needed 614 val s2_spec_new_addr = s2_full_pred.fallThroughAddr + Mux(s2_full_pred.last_may_be_rvi_call, 2.U, 0.U) 615 stack.spec_push_valid := s2_spec_push && !stack_near_overflow 616 stack.spec_pop_valid := s2_spec_pop && !stack_near_overflow 617 stack.spec_push_addr := s2_spec_new_addr 618 619 // confirm that the call/ret is the taken cfi 620 s2_spec_push := io.s2_fire(2) && s2_full_pred.hit_taken_on_call && !io.s3_redirect(2) 621 s2_spec_pop := io.s2_fire(2) && s2_full_pred.hit_taken_on_ret && !io.s3_redirect(2) 622 623 // val s2_jalr_target = io.out.s2.full_pred.jalr_target 624 // val s2_last_target_in = s2_full_pred.targets.last 625 // val s2_last_target_out = io.out.s2.full_pred(2).targets.last 626 val s2_is_jalr = s2_full_pred.is_jalr 627 val s2_is_ret = s2_full_pred.is_ret 628 val s2_top = stack.spec_pop_addr 629 // assert(is_jalr && is_ret || !is_ret) 630 when(s2_is_ret && io.ctrl.ras_enable) { 631 io.out.s2.full_pred.map(_.jalr_target).foreach(_ := s2_top) 632 // FIXME: should use s1 globally 633 } 634 // s2_last_target_out := Mux(s2_is_jalr, s2_jalr_target, s2_last_target_in) 635 io.out.s2.full_pred.zipWithIndex.foreach { case (a, i) => 636 a.targets.last := Mux( 637 s2_is_jalr, 638 io.out.s2.full_pred(i).jalr_target, 639 io.in.bits.resp_in(0).s2.full_pred(i).targets.last 640 ) 641 } 642 643 val s2_meta = Wire(new RASInternalMeta) 644 s2_meta.ssp := stack.ssp 645 s2_meta.sctr := stack.sctr 646 s2_meta.TOSR := stack.TOSR 647 s2_meta.TOSW := stack.TOSW 648 s2_meta.NOS := stack.NOS 649 650 val s3_top = RegEnable(stack.spec_pop_addr, io.s2_fire(2)) 651 val s3_spec_new_addr = RegEnable(s2_spec_new_addr, io.s2_fire(2)) 652 653 // val s3_jalr_target = io.out.s3.full_pred.jalr_target 654 // val s3_last_target_in = io.in.bits.resp_in(0).s3.full_pred(2).targets.last 655 // val s3_last_target_out = io.out.s3.full_pred(2).targets.last 656 val s3_is_jalr = 657 io.in.bits.resp_in(0).s3.full_pred(2).is_jalr && !io.in.bits.resp_in(0).s3.full_pred(2).fallThroughErr 658 val s3_is_ret = io.in.bits.resp_in(0).s3.full_pred(2).is_ret && !io.in.bits.resp_in(0).s3.full_pred(2).fallThroughErr 659 // assert(is_jalr && is_ret || !is_ret) 660 when(s3_is_ret && io.ctrl.ras_enable) { 661 io.out.s3.full_pred.map(_.jalr_target).foreach(_ := s3_top) 662 // FIXME: should use s1 globally 663 } 664 // s3_last_target_out := Mux(s3_is_jalr, s3_jalr_target, s3_last_target_in) 665 io.out.s3.full_pred.zipWithIndex.foreach { case (a, i) => 666 a.targets.last := Mux( 667 s3_is_jalr, 668 io.out.s3.full_pred(i).jalr_target, 669 io.in.bits.resp_in(0).s3.full_pred(i).targets.last 670 ) 671 } 672 673 val s3_pushed_in_s2 = RegEnable(s2_spec_push, io.s2_fire(2)) 674 val s3_popped_in_s2 = RegEnable(s2_spec_pop, io.s2_fire(2)) 675 val s3_push = 676 io.in.bits.resp_in(0).s3.full_pred(2).hit_taken_on_call && !io.in.bits.resp_in(0).s3.full_pred(2).fallThroughErr 677 val s3_pop = 678 io.in.bits.resp_in(0).s3.full_pred(2).hit_taken_on_ret && !io.in.bits.resp_in(0).s3.full_pred(2).fallThroughErr 679 680 val s3_cancel = io.s3_fire(2) && (s3_pushed_in_s2 =/= s3_push || s3_popped_in_s2 =/= s3_pop) 681 stack.s2_fire := io.s2_fire(2) 682 stack.s3_fire := io.s3_fire(2) 683 684 stack.s3_cancel := s3_cancel && !stack_near_overflow 685 686 val s3_meta = RegEnable(s2_meta, io.s2_fire(2)) 687 688 stack.s3_meta := s3_meta 689 stack.s3_missed_pop := s3_pop && !s3_popped_in_s2 690 stack.s3_missed_push := s3_push && !s3_pushed_in_s2 691 stack.s3_pushAddr := s3_spec_new_addr 692 693 // no longer need the top Entry, but TOSR, TOSW, ssp sctr 694 // TODO: remove related signals 695 696 val last_stage_meta = Wire(new RASMeta) 697 last_stage_meta.ssp := s3_meta.ssp 698 last_stage_meta.TOSW := s3_meta.TOSW 699 700 io.out.last_stage_spec_info.sctr := s3_meta.sctr 701 io.out.last_stage_spec_info.ssp := s3_meta.ssp 702 io.out.last_stage_spec_info.TOSW := s3_meta.TOSW 703 io.out.last_stage_spec_info.TOSR := s3_meta.TOSR 704 io.out.last_stage_spec_info.NOS := s3_meta.NOS 705 io.out.last_stage_spec_info.topAddr := s3_top 706 io.out.last_stage_meta := last_stage_meta.asUInt 707 708 val redirect = RegNextWithEnable(io.redirect) 709 val do_recover = redirect.valid 710 val recover_cfi = redirect.bits.cfiUpdate 711 712 val retMissPred = do_recover && redirect.bits.level === 0.U && recover_cfi.pd.isRet && recover_cfi.pd.valid 713 val callMissPred = do_recover && redirect.bits.level === 0.U && recover_cfi.pd.isCall && recover_cfi.pd.valid 714 // when we mispredict a call, we must redo a push operation 715 // similarly, when we mispredict a return, we should redo a pop 716 val stack_TOSW = stack.TOSW 717 val redirect_TOSW = recover_cfi.TOSW 718 stack.redirect_valid := do_recover && (isBefore(redirect_TOSW, stack_TOSW) || !stack_near_overflow) 719 stack.redirect_isCall := callMissPred 720 stack.redirect_isRet := retMissPred 721 stack.redirect_meta_ssp := recover_cfi.ssp 722 stack.redirect_meta_sctr := recover_cfi.sctr 723 stack.redirect_meta_TOSW := recover_cfi.TOSW 724 stack.redirect_meta_TOSR := recover_cfi.TOSR 725 stack.redirect_meta_NOS := recover_cfi.NOS 726 stack.redirect_callAddr := recover_cfi.pc + Mux(recover_cfi.pd.isRVC, 2.U, 4.U) 727 728 val updateValid = RegNext(io.update.valid, init = false.B) 729 val update = Wire(new BranchPredictionUpdate) 730 update := RegEnable(io.update.bits, io.update.valid) 731 732 // The pc register has been moved outside of predictor, pc field of update bundle and other update data are not in the same stage 733 // so io.update.bits.pc is used directly here 734 val update_pc = io.update.bits.pc 735 736 // full core power down sequence need 'val updateMeta = RegEnable(io.update.bits.meta.asTypeOf(new RASMeta), io.update.valid)' to be 737 // 'val updateMeta = RegEnable(io.update.bits.meta.asTypeOf(new RASMeta), io.update.valid && (io.update.bits.is_call || io.update.bits.is_ret))', 738 // but the fault-tolerance mechanism of the return stack needs to be updated in time. Using an unexpected old value on reset will cause errors. 739 // Only 9 registers have clock gate efficiency affected, so we relaxed the control signals. 740 val updateMeta = RegEnable(io.update.bits.meta.asTypeOf(new RASMeta), io.update.valid) 741 742 stack.commit_valid := updateValid 743 stack.commit_push_valid := updateValid && update.is_call_taken 744 stack.commit_pop_valid := updateValid && update.is_ret_taken 745 stack.commit_push_addr := update.ftb_entry.getFallThrough(update_pc) + Mux( 746 update.ftb_entry.last_may_be_rvi_call, 747 2.U, 748 0.U 749 ) 750 stack.commit_meta_TOSW := updateMeta.TOSW 751 stack.commit_meta_ssp := updateMeta.ssp 752 753 XSPerfAccumulate("ras_s3_cancel", s3_cancel) 754 XSPerfAccumulate("ras_redirect_recover", redirect.valid) 755 XSPerfAccumulate("ras_s3_and_redirect_recover_at_the_same_time", s3_cancel && redirect.valid) 756 757 val spec_debug = stack.debug 758 XSDebug(io.s2_fire(2), "----------------RAS----------------\n") 759 XSDebug(io.s2_fire(2), " TopRegister: 0x%x\n", stack.spec_pop_addr) 760 XSDebug(io.s2_fire(2), " index addr ctr nos (spec part)\n") 761 for (i <- 0 until RasSpecSize) { 762 XSDebug( 763 io.s2_fire(2), 764 " (%d) 0x%x %d %d", 765 i.U, 766 spec_debug.spec_queue(i).retAddr, 767 spec_debug.spec_queue(i).ctr, 768 spec_debug.spec_nos(i).value 769 ) 770 XSDebug(io.s2_fire(2) && i.U === stack.TOSW.value, " <----TOSW") 771 XSDebug(io.s2_fire(2) && i.U === stack.TOSR.value, " <----TOSR") 772 XSDebug(io.s2_fire(2) && i.U === stack.BOS.value, " <----BOS") 773 XSDebug(io.s2_fire(2), "\n") 774 } 775 XSDebug(io.s2_fire(2), " index addr ctr (committed part)\n") 776 for (i <- 0 until RasSize) { 777 XSDebug( 778 io.s2_fire(2), 779 " (%d) 0x%x %d", 780 i.U, 781 spec_debug.commit_stack(i).retAddr, 782 spec_debug.commit_stack(i).ctr 783 ) 784 XSDebug(io.s2_fire(2) && i.U === stack.ssp, " <----ssp") 785 XSDebug(io.s2_fire(2) && i.U === stack.nsp, " <----nsp") 786 XSDebug(io.s2_fire(2), "\n") 787 } 788 /* 789 XSDebug(s2_spec_push, "s2_spec_push inAddr: 0x%x inCtr: %d | allocNewEntry:%d | sp:%d \n", 790 s2_spec_new_addr,spec_debug.spec_push_entry.ctr,spec_debug.spec_alloc_new,spec_debug.sp.asUInt) 791 XSDebug(s2_spec_pop, "s2_spec_pop outAddr: 0x%x \n",io.out.s2.getTarget) 792 val s3_recover_entry = spec_debug.recover_push_entry 793 XSDebug(s3_recover && s3_push, "s3_recover_push inAddr: 0x%x inCtr: %d | allocNewEntry:%d | sp:%d \n", 794 s3_recover_entry.retAddr, s3_recover_entry.ctr, spec_debug.recover_alloc_new, s3_sp.asUInt) 795 XSDebug(s3_recover && s3_pop, "s3_recover_pop outAddr: 0x%x \n",io.out.s3.getTarget) 796 val redirectUpdate = redirect.bits.cfiUpdate 797 XSDebug(do_recover && callMissPred, "redirect_recover_push\n") 798 XSDebug(do_recover && retMissPred, "redirect_recover_pop\n") 799 XSDebug(do_recover, "redirect_recover(SP:%d retAddr:%x ctr:%d) \n", 800 redirectUpdate.rasSp,redirectUpdate.rasEntry.retAddr,redirectUpdate.rasEntry.ctr) 801 */ 802 803 generatePerfEvent() 804} 805