xref: /XiangShan/src/main/scala/xiangshan/frontend/newRAS.scala (revision 9928cec7a67130f853d7dc18e380b564782f2954)
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