1package xiangshan.backend 2 3import chisel3._ 4import chisel3.util._ 5import utils._ 6import xiangshan._ 7import xiangshan.backend.decode.{DecodeStage, ImmUnion, WaitTableParameters} 8import xiangshan.backend.rename.{BusyTable, Rename} 9import xiangshan.backend.dispatch.Dispatch 10import xiangshan.backend.exu._ 11import xiangshan.backend.exu.Exu.exuConfigs 12import xiangshan.backend.ftq.{Ftq, FtqRead, GetPcByFtq} 13import xiangshan.backend.regfile.RfReadPort 14import xiangshan.backend.roq.{Roq, RoqCSRIO, RoqLsqIO, RoqPtr} 15import xiangshan.mem.LsqEnqIO 16 17class CtrlToIntBlockIO extends XSBundle { 18 val enqIqCtrl = Vec(exuParameters.IntExuCnt, DecoupledIO(new MicroOp)) 19 val readRf = Vec(NRIntReadPorts, Output(UInt(PhyRegIdxWidth.W))) 20 val jumpPc = Output(UInt(VAddrBits.W)) 21 val jalr_target = Output(UInt(VAddrBits.W)) 22 // int block only uses port 0~7 23 val readPortIndex = Vec(exuParameters.IntExuCnt, Output(UInt(log2Ceil(8 / 2).W))) // TODO parameterize 8 here 24 val redirect = ValidIO(new Redirect) 25 val flush = Output(Bool()) 26} 27 28class CtrlToFpBlockIO extends XSBundle { 29 val enqIqCtrl = Vec(exuParameters.FpExuCnt, DecoupledIO(new MicroOp)) 30 val readRf = Vec(NRFpReadPorts, Output(UInt(PhyRegIdxWidth.W))) 31 // fp block uses port 0~11 32 val readPortIndex = Vec(exuParameters.FpExuCnt, Output(UInt(log2Ceil((NRFpReadPorts - exuParameters.StuCnt) / 3).W))) 33 val redirect = ValidIO(new Redirect) 34 val flush = Output(Bool()) 35} 36 37class CtrlToLsBlockIO extends XSBundle { 38 val enqIqCtrl = Vec(exuParameters.LsExuCnt, DecoupledIO(new MicroOp)) 39 val enqLsq = Flipped(new LsqEnqIO) 40 val waitTableUpdate = Vec(StorePipelineWidth, Input(new WaitTableUpdateReq)) 41 val redirect = ValidIO(new Redirect) 42 val flush = Output(Bool()) 43} 44 45class RedirectGenerator extends XSModule with HasCircularQueuePtrHelper with WaitTableParameters { 46 val io = IO(new Bundle() { 47 val loadRelay = Flipped(ValidIO(new Redirect)) 48 val exuMispredict = Vec(exuParameters.JmpCnt + exuParameters.AluCnt, Flipped(ValidIO(new ExuOutput))) 49 val flush = Input(Bool()) 50 val stage2FtqRead = new FtqRead 51 val stage2Redirect = ValidIO(new Redirect) 52 val stage3Redirect = ValidIO(new Redirect) 53 val waitTableUpdate = Output(new WaitTableUpdateReq) // generated in stage2 54 }) 55 /* 56 LoadQueue Jump ALU0 ALU1 ALU2 ALU3 exception Stage1 57 | | | | | | | 58 |============= reg & compare =====| | ======== 59 | | 60 | | 61 | | Stage2 62 | | 63 redirect (flush backend) | 64 | | 65 === reg === | ======== 66 | | 67 |----- mux (exception first) -----| Stage3 68 | 69 redirect (send to frontend) 70 */ 71 def selectOlderRedirect(x: Valid[Redirect], y: Valid[Redirect]): Valid[Redirect] = { 72 Mux(x.valid, 73 Mux(y.valid, 74 Mux(isAfter(x.bits.roqIdx, y.bits.roqIdx), y, x), 75 x 76 ), 77 y 78 ) 79 } 80 def selectOlderExuOutWithFlag(x: Valid[ExuOutput], y: Valid[ExuOutput]): (Valid[ExuOutput], Bool) = { 81 val yIsOlder = Mux(x.valid, 82 Mux(y.valid, 83 Mux(isAfter(x.bits.redirect.roqIdx, y.bits.redirect.roqIdx), true.B, false.B), 84 false.B 85 ), 86 true.B 87 ) 88 val sel = Mux(yIsOlder, y, x) 89 (sel, yIsOlder) 90 } 91 def selectOlderExuOut(x: Valid[ExuOutput], y: Valid[ExuOutput]): Valid[ExuOutput] = { 92 selectOlderExuOutWithFlag(x, y)._1 93 } 94 val jumpOut = io.exuMispredict.head 95 val oldestAluOut = ParallelOperation(io.exuMispredict.tail, selectOlderExuOut) 96 val (oldestExuOut, jumpIsOlder) = selectOlderExuOutWithFlag(oldestAluOut, jumpOut) // select between jump and alu 97 98 val oldestMispredict = selectOlderRedirect(io.loadRelay, { 99 val redirect = Wire(Valid(new Redirect)) 100 redirect.valid := oldestExuOut.valid 101 redirect.bits := oldestExuOut.bits.redirect 102 redirect 103 }) 104 105 XSDebug(oldestExuOut.valid, p"exuMispredict: ${Binary(Cat(io.exuMispredict.map(_.valid)))}\n") 106 107 val s1_isJump = RegNext(jumpIsOlder, init = false.B) 108 val s1_jumpTarget = RegEnable(jumpOut.bits.redirect.cfiUpdate.target, jumpOut.valid) 109 val s1_imm12_reg = RegEnable(oldestExuOut.bits.uop.ctrl.imm(11, 0), oldestExuOut.valid) 110 val s1_pd = RegEnable(oldestExuOut.bits.uop.cf.pd, oldestExuOut.valid) 111 val s1_redirect_bits_reg = Reg(new Redirect) 112 val s1_redirect_valid_reg = RegInit(false.B) 113 114 // stage1 -> stage2 115 when(oldestMispredict.valid && !oldestMispredict.bits.roqIdx.needFlush(io.stage2Redirect, io.flush)){ 116 s1_redirect_bits_reg := oldestMispredict.bits 117 s1_redirect_valid_reg := true.B 118 }.otherwise({ 119 s1_redirect_valid_reg := false.B 120 }) 121 io.stage2Redirect.valid := s1_redirect_valid_reg && !io.flush 122 io.stage2Redirect.bits := s1_redirect_bits_reg 123 io.stage2Redirect.bits.cfiUpdate := DontCare 124 // at stage2, we read ftq to get pc 125 io.stage2FtqRead.ptr := s1_redirect_bits_reg.ftqIdx 126 127 // stage3, calculate redirect target 128 val s2_isJump = RegNext(s1_isJump) 129 val s2_jumpTarget = RegEnable(s1_jumpTarget, s1_redirect_valid_reg) 130 val s2_imm12_reg = RegEnable(s1_imm12_reg, s1_redirect_valid_reg) 131 val s2_pd = RegEnable(s1_pd, s1_redirect_valid_reg) 132 val s2_redirect_bits_reg = RegEnable(s1_redirect_bits_reg, enable = s1_redirect_valid_reg) 133 val s2_redirect_valid_reg = RegNext(s1_redirect_valid_reg && !io.flush, init = false.B) 134 135 val ftqRead = io.stage2FtqRead.entry 136 val cfiUpdate_pc = 137 Cat(ftqRead.ftqPC.head(VAddrBits - s2_redirect_bits_reg.ftqOffset.getWidth - instOffsetBits), 138 s2_redirect_bits_reg.ftqOffset, 139 0.U(instOffsetBits.W)) 140 val real_pc = 141 GetPcByFtq(ftqRead.ftqPC, s2_redirect_bits_reg.ftqOffset, 142 ftqRead.lastPacketPC.valid, 143 ftqRead.lastPacketPC.bits) 144 val brTarget = real_pc + SignExt(ImmUnion.B.toImm32(s2_imm12_reg), XLEN) 145 val snpc = real_pc + Mux(s2_pd.isRVC, 2.U, 4.U) 146 val isReplay = RedirectLevel.flushItself(s2_redirect_bits_reg.level) 147 val target = Mux(isReplay, 148 real_pc, // repaly from itself 149 Mux(s2_redirect_bits_reg.cfiUpdate.taken, 150 Mux(s2_isJump, s2_jumpTarget, brTarget), 151 snpc 152 ) 153 ) 154 155 // update waittable if load violation redirect triggered 156 io.waitTableUpdate.valid := isReplay && s2_redirect_valid_reg 157 io.waitTableUpdate.waddr := XORFold(real_pc(VAddrBits-1, 1), WaitTableAddrWidth) 158 io.waitTableUpdate.wdata := true.B 159 160 io.stage3Redirect.valid := s2_redirect_valid_reg 161 io.stage3Redirect.bits := s2_redirect_bits_reg 162 val stage3CfiUpdate = io.stage3Redirect.bits.cfiUpdate 163 stage3CfiUpdate.pc := cfiUpdate_pc 164 stage3CfiUpdate.pd := s2_pd 165 stage3CfiUpdate.rasSp := ftqRead.rasSp 166 stage3CfiUpdate.rasEntry := ftqRead.rasTop 167 stage3CfiUpdate.hist := ftqRead.hist 168 stage3CfiUpdate.predHist := ftqRead.predHist 169 stage3CfiUpdate.specCnt := ftqRead.specCnt 170 stage3CfiUpdate.predTaken := s2_redirect_bits_reg.cfiUpdate.predTaken 171 stage3CfiUpdate.sawNotTakenBranch := VecInit((0 until PredictWidth).map{ i => 172 if(i == 0) false.B else Cat(ftqRead.br_mask.take(i)).orR() 173 })(s2_redirect_bits_reg.ftqOffset) 174 stage3CfiUpdate.target := target 175 stage3CfiUpdate.taken := s2_redirect_bits_reg.cfiUpdate.taken 176 stage3CfiUpdate.isMisPred := s2_redirect_bits_reg.cfiUpdate.isMisPred 177} 178 179class CtrlBlock extends XSModule with HasCircularQueuePtrHelper { 180 val io = IO(new Bundle { 181 val frontend = Flipped(new FrontendToBackendIO) 182 val fromIntBlock = Flipped(new IntBlockToCtrlIO) 183 val fromFpBlock = Flipped(new FpBlockToCtrlIO) 184 val fromLsBlock = Flipped(new LsBlockToCtrlIO) 185 val toIntBlock = new CtrlToIntBlockIO 186 val toFpBlock = new CtrlToFpBlockIO 187 val toLsBlock = new CtrlToLsBlockIO 188 val roqio = new Bundle { 189 // to int block 190 val toCSR = new RoqCSRIO 191 val exception = ValidIO(new ExceptionInfo) 192 // to mem block 193 val lsq = new RoqLsqIO 194 } 195 val csrCtrl = Input(new CustomCSRCtrlIO) 196 }) 197 198 val difftestIO = IO(new Bundle() { 199 val fromRoq = new Bundle() { 200 val commit = Output(UInt(32.W)) 201 val thisPC = Output(UInt(XLEN.W)) 202 val thisINST = Output(UInt(32.W)) 203 val skip = Output(UInt(32.W)) 204 val wen = Output(UInt(32.W)) 205 val wdata = Output(Vec(CommitWidth, UInt(XLEN.W))) // set difftest width to 6 206 val wdst = Output(Vec(CommitWidth, UInt(32.W))) // set difftest width to 6 207 val wpc = Output(Vec(CommitWidth, UInt(XLEN.W))) // set difftest width to 6 208 val isRVC = Output(UInt(32.W)) 209 val scFailed = Output(Bool()) 210 val lpaddr = Output(Vec(CommitWidth, UInt(64.W))) 211 val ltype = Output(Vec(CommitWidth, UInt(32.W))) 212 val lfu = Output(Vec(CommitWidth, UInt(4.W))) 213 } 214 }) 215 difftestIO <> DontCare 216 217 val ftq = Module(new Ftq) 218 val trapIO = IO(new TrapIO()) 219 trapIO <> DontCare 220 221 val decode = Module(new DecodeStage) 222 val rename = Module(new Rename) 223 val dispatch = Module(new Dispatch) 224 val intBusyTable = Module(new BusyTable(NRIntReadPorts, NRIntWritePorts)) 225 val fpBusyTable = Module(new BusyTable(NRFpReadPorts, NRFpWritePorts)) 226 val redirectGen = Module(new RedirectGenerator) 227 228 val roqWbSize = NRIntWritePorts + NRFpWritePorts + exuParameters.StuCnt 229 val roq = Module(new Roq(roqWbSize)) 230 231 val backendRedirect = redirectGen.io.stage2Redirect 232 val frontendRedirect = redirectGen.io.stage3Redirect 233 val flush = roq.io.flushOut.valid 234 val flushReg = RegNext(flush) 235 236 redirectGen.io.exuMispredict.zip(io.fromIntBlock.exuRedirect).map({case (x, y) => 237 val misPred = y.valid && y.bits.redirect.cfiUpdate.isMisPred 238 val killedByOlder = y.bits.uop.roqIdx.needFlush(backendRedirect, flushReg) 239 x.valid := RegNext(misPred && !killedByOlder, init = false.B) 240 x.bits := RegEnable(y.bits, y.valid) 241 }) 242 redirectGen.io.loadRelay := io.fromLsBlock.replay 243 redirectGen.io.flush := flushReg 244 245 ftq.io.enq <> io.frontend.fetchInfo 246 for(i <- 0 until CommitWidth){ 247 ftq.io.roq_commits(i).valid := roq.io.commits.valid(i) && !roq.io.commits.isWalk 248 ftq.io.roq_commits(i).bits := roq.io.commits.info(i) 249 } 250 ftq.io.redirect <> backendRedirect 251 ftq.io.flush := flushReg 252 ftq.io.flushIdx := RegNext(roq.io.flushOut.bits.ftqIdx) 253 ftq.io.flushOffset := RegNext(roq.io.flushOut.bits.ftqOffset) 254 ftq.io.frontendRedirect <> frontendRedirect 255 ftq.io.exuWriteback <> io.fromIntBlock.exuRedirect 256 257 ftq.io.ftqRead(1) <> redirectGen.io.stage2FtqRead 258 ftq.io.ftqRead(2).ptr := roq.io.flushOut.bits.ftqIdx 259 val flushPC = GetPcByFtq( 260 ftq.io.ftqRead(2).entry.ftqPC, 261 RegEnable(roq.io.flushOut.bits.ftqOffset, roq.io.flushOut.valid), 262 ftq.io.ftqRead(2).entry.lastPacketPC.valid, 263 ftq.io.ftqRead(2).entry.lastPacketPC.bits 264 ) 265 266 val flushRedirect = Wire(Valid(new Redirect)) 267 flushRedirect.valid := flushReg 268 flushRedirect.bits := DontCare 269 flushRedirect.bits.ftqIdx := RegEnable(roq.io.flushOut.bits.ftqIdx, flush) 270 flushRedirect.bits.interrupt := true.B 271 flushRedirect.bits.cfiUpdate.target := Mux(io.roqio.toCSR.isXRet || roq.io.exception.valid, 272 io.roqio.toCSR.trapTarget, 273 flushPC + 4.U // flush pipe 274 ) 275 276 io.frontend.redirect_cfiUpdate := Mux(flushRedirect.valid, flushRedirect, frontendRedirect) 277 io.frontend.commit_cfiUpdate := ftq.io.commit_ftqEntry 278 io.frontend.ftqEnqPtr := ftq.io.enqPtr 279 io.frontend.ftqLeftOne := ftq.io.leftOne 280 281 decode.io.in <> io.frontend.cfVec 282 // currently, we only update wait table when isReplay 283 decode.io.waitTableUpdate(0) <> RegNext(redirectGen.io.waitTableUpdate) 284 decode.io.waitTableUpdate(1) := DontCare 285 decode.io.waitTableUpdate(1).valid := false.B 286 // decode.io.waitTableUpdate <> io.toLsBlock.waitTableUpdate 287 decode.io.csrCtrl := RegNext(io.csrCtrl) 288 289 290 val jumpInst = dispatch.io.enqIQCtrl(0).bits 291 val ftqOffsetReg = Reg(UInt(log2Up(PredictWidth).W)) 292 ftqOffsetReg := jumpInst.cf.ftqOffset 293 ftq.io.ftqRead(0).ptr := jumpInst.cf.ftqPtr // jump 294 io.toIntBlock.jumpPc := GetPcByFtq( 295 ftq.io.ftqRead(0).entry.ftqPC, ftqOffsetReg, 296 ftq.io.ftqRead(0).entry.lastPacketPC.valid, 297 ftq.io.ftqRead(0).entry.lastPacketPC.bits 298 ) 299 io.toIntBlock.jalr_target := ftq.io.ftqRead(0).entry.target 300 301 // pipeline between decode and dispatch 302 for (i <- 0 until RenameWidth) { 303 PipelineConnect(decode.io.out(i), rename.io.in(i), rename.io.in(i).ready, 304 io.frontend.redirect_cfiUpdate.valid) 305 } 306 307 rename.io.redirect <> backendRedirect 308 rename.io.flush := flushReg 309 rename.io.roqCommits <> roq.io.commits 310 rename.io.out <> dispatch.io.fromRename 311 rename.io.renameBypass <> dispatch.io.renameBypass 312 rename.io.dispatchInfo <> dispatch.io.preDpInfo 313 314 dispatch.io.redirect <> backendRedirect 315 dispatch.io.flush := flushReg 316 dispatch.io.enqRoq <> roq.io.enq 317 dispatch.io.enqLsq <> io.toLsBlock.enqLsq 318 dispatch.io.readIntRf <> io.toIntBlock.readRf 319 dispatch.io.readFpRf <> io.toFpBlock.readRf 320 dispatch.io.allocPregs.zipWithIndex.foreach { case (preg, i) => 321 intBusyTable.io.allocPregs(i).valid := preg.isInt 322 fpBusyTable.io.allocPregs(i).valid := preg.isFp 323 intBusyTable.io.allocPregs(i).bits := preg.preg 324 fpBusyTable.io.allocPregs(i).bits := preg.preg 325 } 326 dispatch.io.numExist <> io.fromIntBlock.numExist ++ io.fromFpBlock.numExist ++ io.fromLsBlock.numExist 327 dispatch.io.enqIQCtrl <> io.toIntBlock.enqIqCtrl ++ io.toFpBlock.enqIqCtrl ++ io.toLsBlock.enqIqCtrl 328// dispatch.io.enqIQData <> io.toIntBlock.enqIqData ++ io.toFpBlock.enqIqData ++ io.toLsBlock.enqIqData 329 330 331 fpBusyTable.io.flush := flushReg 332 intBusyTable.io.flush := flushReg 333 for((wb, setPhyRegRdy) <- io.fromIntBlock.wbRegs.zip(intBusyTable.io.wbPregs)){ 334 setPhyRegRdy.valid := wb.valid && wb.bits.uop.ctrl.rfWen 335 setPhyRegRdy.bits := wb.bits.uop.pdest 336 } 337 for((wb, setPhyRegRdy) <- io.fromFpBlock.wbRegs.zip(fpBusyTable.io.wbPregs)){ 338 setPhyRegRdy.valid := wb.valid && wb.bits.uop.ctrl.fpWen 339 setPhyRegRdy.bits := wb.bits.uop.pdest 340 } 341 intBusyTable.io.read <> dispatch.io.readIntState 342 fpBusyTable.io.read <> dispatch.io.readFpState 343 344 roq.io.redirect <> backendRedirect 345 roq.io.exeWbResults <> (io.fromIntBlock.wbRegs ++ io.fromFpBlock.wbRegs ++ io.fromLsBlock.stOut) 346 347 // TODO: is 'backendRedirect' necesscary? 348 io.toIntBlock.redirect <> backendRedirect 349 io.toIntBlock.flush <> flushReg 350 io.toFpBlock.redirect <> backendRedirect 351 io.toFpBlock.flush <> flushReg 352 io.toLsBlock.redirect <> backendRedirect 353 io.toLsBlock.flush <> flushReg 354 355 if (!env.FPGAPlatform) { 356 difftestIO.fromRoq <> roq.difftestIO 357 trapIO <> roq.trapIO 358 } 359 360 dispatch.io.readPortIndex.intIndex <> io.toIntBlock.readPortIndex 361 dispatch.io.readPortIndex.fpIndex <> io.toFpBlock.readPortIndex 362 363 // roq to int block 364 io.roqio.toCSR <> roq.io.csr 365 io.roqio.exception := roq.io.exception 366 io.roqio.exception.bits.uop.cf.pc := flushPC 367 // roq to mem block 368 io.roqio.lsq <> roq.io.lsq 369} 370