xref: /XiangShan/src/main/scala/xiangshan/frontend/RAS.scala (revision c6d439803a044ea209139672b25e35fe8d7f4aa0)
1/***************************************************************************************
2* Copyright (c) 2020-2021 Institute of Computing Technology, Chinese Academy of Sciences
3*
4* XiangShan is licensed under Mulan PSL v2.
5* You can use this software according to the terms and conditions of the Mulan PSL v2.
6* You may obtain a copy of Mulan PSL v2 at:
7*          http://license.coscl.org.cn/MulanPSL2
8*
9* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
10* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
11* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
12*
13* See the Mulan PSL v2 for more details.
14***************************************************************************************/
15
16package xiangshan.frontend
17
18import chipsalliance.rocketchip.config.Parameters
19import chisel3._
20import chisel3.util._
21import xiangshan._
22import utils._
23import chisel3.experimental.chiselName
24import scala.tools.nsc.doc.base.comment.Bold
25
26class RASEntry()(implicit p: Parameters) extends XSBundle {
27    val retAddr = UInt(VAddrBits.W)
28    val ctr = UInt(8.W) // layer of nested call functions
29}
30
31@chiselName
32class RAS(implicit p: Parameters) extends BasePredictor
33{
34    class RASResp extends Resp
35    {
36        val target =UInt(VAddrBits.W)
37    }
38
39    class RASBranchInfo extends Meta
40    {
41        val rasSp = UInt(log2Up(RasSize).W)
42        val rasTop = new RASEntry
43    }
44
45    class RASIO extends DefaultBasePredictorIO
46    {
47        val is_ret = Input(Bool())
48        val callIdx = Flipped(ValidIO(UInt(log2Ceil(PredictWidth).W)))
49        val isRVC = Input(Bool())
50        val isLastHalfRVI = Input(Bool())
51        val redirect =  Flipped(ValidIO(new Redirect))
52        val out = Output(new RASResp)
53        val meta = Output(new RASBranchInfo)
54    }
55
56
57    def rasEntry() = new RASEntry
58
59    object RASEntry {
60        def apply(retAddr: UInt, ctr: UInt): RASEntry = {
61            val e = Wire(rasEntry())
62            e.retAddr := retAddr
63            e.ctr := ctr
64            e
65        }
66    }
67
68    override val io = IO(new RASIO)
69    override val debug = true
70
71    @chiselName
72    class RASStack(val rasSize: Int) extends XSModule {
73        val io = IO(new Bundle {
74            val push_valid = Input(Bool())
75            val pop_valid = Input(Bool())
76            val spec_new_addr = Input(UInt(VAddrBits.W))
77
78            val recover_sp = Input(UInt(log2Up(rasSize).W))
79            val recover_top = Input(rasEntry())
80            val recover_valid = Input(Bool())
81            val recover_push = Input(Bool())
82            val recover_pop = Input(Bool())
83            val recover_new_addr = Input(UInt(VAddrBits.W))
84
85            val sp = Output(UInt(log2Up(rasSize).W))
86            val top = Output(rasEntry())
87        })
88        val debugIO = IO(new Bundle{
89            val push_entry = Output(rasEntry())
90            val alloc_new = Output(Bool())
91            val sp = Output(UInt(log2Up(rasSize).W))
92            val topRegister = Output(rasEntry())
93            val out_mem = Output(Vec(RasSize, rasEntry()))
94        })
95
96        val stack = Mem(RasSize, new RASEntry)
97        val sp = RegInit(0.U(log2Up(rasSize).W))
98        val top = RegInit(0.U.asTypeOf(new RASEntry))
99        val topPtr = RegInit(0.U(log2Up(rasSize).W))
100
101        def ptrInc(ptr: UInt) = Mux(ptr === (rasSize-1).U, 0.U, ptr + 1.U)
102        def ptrDec(ptr: UInt) = Mux(ptr === 0.U, (rasSize-1).U, ptr - 1.U)
103
104        val alloc_new = io.spec_new_addr =/= top.retAddr || top.ctr.andR
105        val recover_alloc_new = io.recover_new_addr =/= io.recover_top.retAddr || io.recover_top.ctr.andR
106
107        // TODO: fix overflow and underflow bugs
108        def update(recover: Bool)(do_push: Bool, do_pop: Bool, do_alloc_new: Bool,
109            do_sp: UInt, do_top_ptr: UInt, do_new_addr: UInt,
110            do_top: RASEntry) = {
111                when (do_push) {
112                    when (do_alloc_new) {
113                        sp     := ptrInc(do_sp)
114                        topPtr := do_sp
115                        top.retAddr := do_new_addr
116                        top.ctr := 1.U
117                        stack.write(do_sp, RASEntry(do_new_addr, 1.U))
118                    }.otherwise {
119                        when (recover) {
120                            sp := do_sp
121                            topPtr := do_top_ptr
122                            top.retAddr := do_top.retAddr
123                        }
124                        top.ctr := do_top.ctr + 1.U
125                        stack.write(do_top_ptr, RASEntry(do_new_addr, do_top.ctr + 1.U))
126                    }
127                }.elsewhen (do_pop) {
128                    when (do_top.ctr === 1.U) {
129                        sp     := ptrDec(do_sp)
130                        topPtr := ptrDec(do_top_ptr)
131                        top := stack.read(ptrDec(do_top_ptr))
132                    }.otherwise {
133                        when (recover) {
134                            sp := do_sp
135                            topPtr := do_top_ptr
136                            top.retAddr := do_top.retAddr
137                        }
138                        top.ctr := do_top.ctr - 1.U
139                        stack.write(do_top_ptr, RASEntry(do_top.retAddr, do_top.ctr - 1.U))
140                    }
141                }.otherwise {
142                    when (recover) {
143                        sp := do_sp
144                        topPtr := do_top_ptr
145                        top := do_top
146                        stack.write(do_top_ptr, do_top)
147                    }
148                }
149                XSPerfAccumulate("ras_overflow", do_push && do_alloc_new && ptrInc(do_sp) === 0.U)
150                XSPerfAccumulate("ras_underflow", do_pop && do_top.ctr === 1.U && ptrDec(do_sp) === (rasSize-1).U)
151            }
152
153        update(io.recover_valid)(
154            Mux(io.recover_valid, io.recover_push,     io.push_valid),
155            Mux(io.recover_valid, io.recover_pop,      io.pop_valid),
156            Mux(io.recover_valid, recover_alloc_new,   alloc_new),
157            Mux(io.recover_valid, io.recover_sp,       sp),
158            Mux(io.recover_valid, io.recover_sp - 1.U, topPtr),
159            Mux(io.recover_valid, io.recover_new_addr, io.spec_new_addr),
160            Mux(io.recover_valid, io.recover_top,      top))
161
162        io.sp := sp
163        io.top := top
164
165        debugIO.push_entry := RASEntry(io.spec_new_addr, Mux(alloc_new, 1.U, top.ctr + 1.U))
166        debugIO.alloc_new := alloc_new
167        debugIO.sp := sp
168        debugIO.topRegister := top
169        for (i <- 0 until RasSize) {
170            debugIO.out_mem(i) := stack.read(i.U)
171        }
172
173    }
174
175    val spec = Module(new RASStack(RasSize))
176    val spec_ras = spec.io
177
178
179    val spec_push = WireInit(false.B)
180    val spec_pop = WireInit(false.B)
181    val jump_is_first = io.callIdx.bits === 0.U
182    val call_is_last_half = io.isLastHalfRVI && jump_is_first
183    val spec_new_addr = packetAligned(io.pc.bits) + (io.callIdx.bits << instOffsetBits.U) + Mux( (io.isRVC | call_is_last_half) && HasCExtension.B, 2.U, 4.U)
184    spec_ras.push_valid := spec_push
185    spec_ras.pop_valid  := spec_pop
186    spec_ras.spec_new_addr   := spec_new_addr
187    val spec_top_addr = spec_ras.top.retAddr
188
189    spec_push := io.callIdx.valid && io.pc.valid
190    spec_pop  := io.is_ret && io.pc.valid
191
192    val redirect = RegNext(io.redirect)
193    val copy_valid = redirect.valid
194    val recover_cfi = redirect.bits.cfiUpdate
195
196    val retMissPred  = copy_valid && redirect.bits.level === 0.U && recover_cfi.pd.isRet
197    val callMissPred = copy_valid && redirect.bits.level === 0.U && recover_cfi.pd.isCall
198    // when we mispredict a call, we must redo a push operation
199    // similarly, when we mispredict a return, we should redo a pop
200    spec_ras.recover_valid := copy_valid
201    spec_ras.recover_push := callMissPred
202    spec_ras.recover_pop  := retMissPred
203
204    spec_ras.recover_sp  := recover_cfi.rasSp
205    spec_ras.recover_top := recover_cfi.rasEntry
206    spec_ras.recover_new_addr := recover_cfi.pc + Mux(recover_cfi.pd.isRVC, 2.U, 4.U)
207
208    io.meta.rasSp := spec_ras.sp
209    io.meta.rasTop := spec_ras.top
210
211    io.out.target := spec_top_addr
212    // TODO: back-up stack for ras
213    // use checkpoint to recover RAS
214
215    if (BPUDebug && debug) {
216        val spec_debug = spec.debugIO
217        XSDebug("----------------RAS----------------\n")
218        XSDebug(" TopRegister: 0x%x   %d \n",spec_debug.topRegister.retAddr,spec_debug.topRegister.ctr)
219        XSDebug("  index       addr           ctr \n")
220        for(i <- 0 until RasSize){
221            XSDebug("  (%d)   0x%x      %d",i.U,spec_debug.out_mem(i).retAddr,spec_debug.out_mem(i).ctr)
222            when(i.U === spec_debug.sp){XSDebug(false,true.B,"   <----sp")}
223            XSDebug(false,true.B,"\n")
224        }
225        XSDebug(spec_push, "(spec_ras)push  inAddr: 0x%x  inCtr: %d |  allocNewEntry:%d |   sp:%d \n",
226            spec_new_addr,spec_debug.push_entry.ctr,spec_debug.alloc_new,spec_debug.sp.asUInt)
227        XSDebug(spec_pop, "(spec_ras)pop  outAddr: 0x%x \n",io.out.target)
228        val redirectUpdate = redirect.bits.cfiUpdate
229        XSDebug("copyValid:%d recover(SP:%d retAddr:%x ctr:%d) \n",
230            copy_valid,redirectUpdate.rasSp,redirectUpdate.rasEntry.retAddr,redirectUpdate.rasEntry.ctr)
231    }
232
233}
234