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