StatusArray.scala 13.3 KB
Newer Older
L
Lemover 已提交
1 2
/***************************************************************************************
* Copyright (c) 2020-2021 Institute of Computing Technology, Chinese Academy of Sciences
Y
Yinan Xu 已提交
3
* Copyright (c) 2020-2021 Peng Cheng Laboratory
L
Lemover 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16
*
* XiangShan is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

17 18 19 20 21 22 23
package xiangshan.backend.issue

import chipsalliance.rocketchip.config.Parameters
import chisel3._
import chisel3.util._
import xiangshan._
import utils._
Y
Yinan Xu 已提交
24
import xiangshan.backend.rob.RobPtr
25 26
import xiangshan.mem.SqPtr

27
class StatusArrayUpdateIO(params: RSParams)(implicit p: Parameters) extends Bundle {
28 29
  val enable = Input(Bool())
  // should be one-hot
30 31
  val addr = Input(UInt(params.numEntries.W))
  val data = Input(new StatusEntry(params))
32 33 34 35 36 37

  def isLegal() = {
    PopCount(addr.asBools) === 0.U
  }

  override def cloneType: StatusArrayUpdateIO.this.type =
38
    new StatusArrayUpdateIO(params).asInstanceOf[this.type]
39 40
}

41
class StatusEntry(params: RSParams)(implicit p: Parameters) extends XSBundle {
42 43 44 45 46
  // states
  val valid = Bool()
  val scheduled = Bool()
  val blocked = Bool()
  val credit = UInt(4.W)
47
  val srcState = Vec(params.numSrc, Bool())
48
  val midState = Bool()
49
  // data
50 51
  val psrc = Vec(params.numSrc, UInt(params.dataIdBits.W))
  val srcType = Vec(params.numSrc, SrcType())
Y
Yinan Xu 已提交
52
  val robIdx = new RobPtr
53
  val sqIdx = new SqPtr
54 55
  // misc
  val isFirstIssue = Bool()
56

57 58 59
  def canIssue: Bool = {
    val scheduledCond = if (params.needScheduledBit) !scheduled else true.B
    val blockedCond = if (params.checkWaitBit) !blocked else true.B
60 61 62 63 64 65 66 67
    val checkedSrcState = if (params.numSrc > 2) srcState.take(2) else srcState
    val midStateReady = if (params.hasMidState) srcState.last && midState else false.B
    (VecInit(checkedSrcState).asUInt.andR && scheduledCond || midStateReady) && blockedCond
  }

  def allSrcReady: Bool = {
    val midStateReady = if (params.hasMidState) srcState.last && midState else false.B
    srcState.asUInt.andR || midStateReady
68 69
  }

70
  override def cloneType: StatusEntry.this.type =
71
    new StatusEntry(params).asInstanceOf[this.type]
72
  override def toPrintable: Printable = {
Y
Yinan Xu 已提交
73
    p"$valid, $scheduled, ${Binary(srcState.asUInt)}, $psrc, $robIdx"
74 75 76
  }
}

77
class StatusArray(params: RSParams)(implicit p: Parameters) extends XSModule
78 79 80 81 82
  with HasCircularQueuePtrHelper {
  val io = IO(new Bundle {
    val redirect = Flipped(ValidIO(new Redirect))
    val flush = Input(Bool())
    // current status
83 84
    val isValid = Output(UInt(params.numEntries.W))
    val canIssue = Output(UInt(params.numEntries.W))
85
    val flushed = Output(UInt(params.numEntries.W))
86
    // enqueue, dequeue, wakeup, flush
87
    val update = Vec(params.numEnq, new StatusArrayUpdateIO(params))
88 89
    val wakeup = Vec(params.allWakeup, Flipped(ValidIO(new MicroOp)))
    val wakeupMatch = Vec(params.numEntries, Vec(params.numSrc, Output(UInt(params.allWakeup.W))))
90
    val issueGranted = Vec(params.numDeq, Flipped(ValidIO(UInt(params.numEntries.W))))
91 92
    // TODO: if more info is needed, put them in a bundle
    val isFirstIssue = Vec(params.numDeq, Output(Bool()))
93 94
    val allSrcReady = Vec(params.numDeq, Output(Bool()))
    val updateMidState = Input(UInt(params.numEntries.W))
95 96
    val deqRespWidth = if (params.hasFeedback) params.numDeq * 2 else params.numDeq
    val deqResp = Vec(deqRespWidth, Flipped(ValidIO(new Bundle {
97
      val rsMask = UInt(params.numEntries.W)
98
      val success = Bool()
99
      val resptype = RSFeedbackType() // update credit if needs replay
100
    })))
101
    val stIssuePtr = if (params.checkWaitBit) Input(new SqPtr()) else null
102 103
  })

104
  val statusArray = Reg(Vec(params.numEntries, new StatusEntry(params)))
105 106 107 108 109 110 111
  val statusArrayNext = WireInit(statusArray)
  statusArray := statusArrayNext
  when (reset.asBool) {
    statusArray.map(_.valid := false.B)
  }

  // instruction is ready for issue
112 113
  val readyVec = VecInit(statusArray.map(_.canIssue))
  val readyVecNext = VecInit(statusArrayNext.map(_.canIssue))
114 115

  // update srcState when enqueue, wakeup
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
  // For better timing, we use different conditions for data write and srcState update
  def wakeupMatch(srcInfo: (UInt, UInt)): (Bool, UInt) = {
    val (psrc, srcType) = srcInfo
    val (stateMatchVec, dataMatchVec) = io.wakeup.map(w => {
      val pdestMatch = w.valid && w.bits.pdest === psrc
      val rfStateMatch = if (params.exuCfg.get.readIntRf) w.bits.ctrl.rfWen else false.B
      val rfDataMatch = if (params.exuCfg.get.readIntRf) w.bits.ctrl.rfWen && psrc =/= 0.U else false.B
      val fpMatch = if (params.exuCfg.get.readFpRf) w.bits.ctrl.fpWen else false.B
      // For state condition: only pdest is used for matching.
      // If the exu needs both int and fp sources, we need to check which type of source it is.
      // Otherwise, no need to check the source type (does not matter if it is imm).
      val bothIntFp = params.exuCfg.get.readIntRf && params.exuCfg.get.readFpRf
      val bothStateMatch = (rfStateMatch && !SrcType.regIsFp(srcType)) || (fpMatch && SrcType.regIsFp(srcType))
      val stateCond = pdestMatch && (if (bothIntFp) bothStateMatch else rfStateMatch || fpMatch)
      // For data condition: types are matched and int pdest is not $zero.
      val bothDataMatch = (rfDataMatch && SrcType.isReg(srcType)) || (fpMatch && SrcType.isFp(srcType))
      val dataCond = pdestMatch && bothDataMatch
      (stateCond, dataCond)
    }).unzip
    val stateMatch = VecInit(stateMatchVec).asUInt.orR
    val dataMatch = VecInit(dataMatchVec).asUInt
    XSError(PopCount(dataMatchVec) > 1.U, p"matchVec ${Binary(dataMatch)} should be one-hot\n")
    (stateMatch, dataMatch)
139
  }
140

141
  def deqRespSel(i: Int) : (Bool, Bool, UInt) = {
142 143
    val mask = VecInit(io.deqResp.map(resp => resp.valid && resp.bits.rsMask(i)))
    XSError(PopCount(mask) > 1.U, p"feedbackVec ${Binary(mask.asUInt)} should be one-hot\n")
144 145 146 147 148
    val deqValid = mask.asUInt.orR
    XSError(deqValid && !statusArray(i).valid, p"should not deq an invalid entry $i\n")
    if (params.hasFeedback) {
      XSError(deqValid && !statusArray(i).scheduled, p"should not deq an un-scheduled entry $i\n")
    }
149
    val successVec = io.deqResp.map(_.bits.success)
150 151
    val respTypeVec = io.deqResp.map(_.bits.resptype)
    (mask.asUInt.orR, Mux1H(mask, successVec), Mux1H(mask, respTypeVec))
152
  }
153 154 155 156 157 158 159 160

  def enqUpdate(i: Int): (Bool, StatusEntry) = {
    val updateVec = VecInit(io.update.map(u => u.enable && u.addr(i)))
    val updateStatus = Mux1H(updateVec, io.update.map(_.data))
    XSError(PopCount(updateVec) > 1.U, "should not update the same entry\n")
    (updateVec.asUInt.orR, updateStatus)
  }

161
  val flushedVec = Wire(Vec(params.numEntries, Bool()))
162 163 164 165

  val (updateValid, updateVal) = statusArray.indices.map(enqUpdate).unzip
  val deqResp = statusArray.indices.map(deqRespSel)

166
  for (((status, statusNext), i) <- statusArray.zip(statusArrayNext).zipWithIndex) {
167 168
    // valid: when the entry holds a valid instruction, mark it true.
    // Set when (1) not (flushed or deq); AND (2) update.
Y
Yinan Xu 已提交
169
    val isFlushed = status.valid && status.robIdx.needFlush(io.redirect, io.flush)
170 171 172 173 174 175 176 177 178 179 180
    val (deqRespValid, deqRespSucc, deqRespType) = deqResp(i)
    flushedVec(i) := isFlushed || (deqRespValid && deqRespSucc)
    val realUpdateValid = updateValid(i) && !io.redirect.valid && !io.flush
    statusNext.valid := !flushedVec(i) && (realUpdateValid || status.valid)
    XSError(updateValid(i) && status.valid, p"should not update a valid entry $i\n")

    // scheduled: when the entry is scheduled for issue, mark it true.
    // Set when (1) scheduled for issue; (2) enq blocked.
    // Reset when (1) deq is not granted (it needs to be scheduled again); (2) only one credit left.
    val hasIssued = VecInit(io.issueGranted.map(iss => iss.valid && iss.bits(i))).asUInt.orR
    val deqNotGranted = deqRespValid && !deqRespSucc
181
    statusNext.scheduled := false.B
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
    if (params.needScheduledBit) {
      // An entry keeps in the scheduled state until its credit comes to zero or deqFailed.
      val noCredit = status.valid && status.credit === 1.U
      val keepScheduled = status.scheduled && !deqNotGranted && !noCredit
      statusNext.scheduled := Mux(updateValid(i), updateVal(i).scheduled, hasIssued || keepScheduled)
    }
    XSError(hasIssued && !status.valid, p"should not issue an invalid entry $i\n")

    // blocked: indicate whether the entry is blocked for issue until certain conditions meet.
    statusNext.blocked := false.B
    if (params.checkWaitBit) {
      val blockReleased = isAfter(statusNext.sqIdx, io.stIssuePtr)
      statusNext.blocked := Mux(updateValid(i), updateVal(i).blocked, status.blocked) && blockReleased
      when (deqNotGranted && deqRespType === RSFeedbackType.dataInvalid) {
        statusNext.blocked := true.B
        XSError(status.valid && !isAfter(status.sqIdx, RegNext(RegNext(io.stIssuePtr))),
          "Previous store instructions are all issued. Should not trigger dataInvalid.\n")
199
      }
200 201
    }

202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
    // credit: the number of cycles this entry needed until it can be scheduled
    val creditStep = Mux(status.credit > 0.U, status.credit - 1.U, status.credit)
    statusNext.credit := Mux(updateValid(i), updateVal(i).credit, creditStep)
    XSError(status.valid && status.credit > 0.U && !status.scheduled,
      p"instructions $i with credit ${status.credit} must not be scheduled\n")

    // srcState: indicate whether the operand is ready for issue
    val (stateWakeupEn, dataWakeupEnVec) = statusNext.psrc.zip(statusNext.srcType).map(wakeupMatch).unzip
    io.wakeupMatch(i) := dataWakeupEnVec
    // For best timing of srcState, we don't care whether the instruction is valid or not.
    // We also don't care whether the instruction can really enqueue.
    val updateSrcState = updateVal(i).srcState
    val wakeupSrcState = stateWakeupEn
    statusNext.srcState := VecInit(status.srcState.zip(updateSrcState).zip(wakeupSrcState).map {
      // When the instruction enqueues, we always use the wakeup result.
      case ((current, update), wakeup) => wakeup || Mux(updateValid(i), update, current)
    })

220 221 222
    // midState: reset when enqueue; set when receiving feedback
    statusNext.midState := !updateValid(i) && (io.updateMidState(i) || status.midState)

223 224 225
    // static data fields (only updated when instructions enqueue)
    statusNext.psrc := Mux(updateValid(i), updateVal(i).psrc, status.psrc)
    statusNext.srcType := Mux(updateValid(i), updateVal(i).srcType, status.srcType)
Y
Yinan Xu 已提交
226
    statusNext.robIdx := Mux(updateValid(i), updateVal(i).robIdx, status.robIdx)
227 228 229
    statusNext.sqIdx := Mux(updateValid(i), updateVal(i).sqIdx, status.sqIdx)

    // isFirstIssue: indicate whether the entry has been issued before
230 231
    // When the entry is not granted to issue, set isFirstIssue to false.B
    statusNext.isFirstIssue := Mux(hasIssued, false.B, updateValid(i) || status.isFirstIssue)
232

233 234 235 236
    XSDebug(status.valid, p"entry[$i]: $status\n")
  }

  io.isValid := VecInit(statusArray.map(_.valid)).asUInt
237
  io.canIssue := VecInit(statusArrayNext.map(_.valid).zip(readyVecNext).map{ case (v, r) => v && r}).asUInt
238
  io.isFirstIssue := VecInit(io.issueGranted.map(iss => Mux1H(iss.bits, statusArray.map(_.isFirstIssue))))
239
  io.allSrcReady := VecInit(io.issueGranted.map(iss => Mux1H(iss.bits, statusArray.map(_.allSrcReady))))
240
  io.flushed := flushedVec.asUInt
241

242 243 244 245 246 247
  val validEntries = PopCount(statusArray.map(_.valid))
  XSPerfHistogram("valid_entries", validEntries, true.B, 0, params.numEntries, 1)
  for (i <- 0 until params.numSrc) {
    val waitSrc = statusArray.map(_.srcState).map(s => Cat(s.zipWithIndex.filter(_._2 != i).map(_._1)).andR && !s(i))
    val srcBlockIssue = statusArray.zip(waitSrc).map{ case (s, w) => s.valid && !s.scheduled && !s.blocked && w }
    XSPerfAccumulate(s"wait_for_src_$i", PopCount(srcBlockIssue))
248 249 250 251 252 253 254 255 256 257 258
    for (j <- 0 until params.allWakeup) {
      val wakeup_j_i = io.wakeupMatch.map(_(i)(j)).zip(statusArray.map(_.valid)).map(p => p._1 && p._2)
      XSPerfAccumulate(s"wakeup_${j}_$i", PopCount(wakeup_j_i).asUInt)
      val criticalWakeup = srcBlockIssue.zip(wakeup_j_i).map(x => x._1 && x._2)
      XSPerfAccumulate(s"critical_wakeup_${j}_$i", PopCount(criticalWakeup))
      // For FMAs only: critical_wakeup from fma instructions (to fma instructions)
      if (i == 2 && j < 2 * exuParameters.FmacCnt) {
        val isFMA = io.wakeup(j).bits.ctrl.fpu.ren3
        XSPerfAccumulate(s"critical_wakeup_from_fma_${j}", Mux(isFMA, PopCount(criticalWakeup), 0.U))
      }
    }
259
  }
260 261
  val canIssueEntries = PopCount(io.canIssue)
  XSPerfHistogram("can_issue_entries", canIssueEntries, true.B, 0, params.numEntries, 1)
262 263 264 265 266 267
  val isBlocked = PopCount(statusArray.map(s => s.valid && s.blocked))
  XSPerfAccumulate("blocked_entries", isBlocked)
  val isScheduled = PopCount(statusArray.map(s => s.valid && s.scheduled))
  XSPerfAccumulate("scheduled_entries", isScheduled)
  val notSelected = PopCount(io.canIssue) - PopCount(io.issueGranted.map(_.valid))
  XSPerfAccumulate("not_selected_entries", notSelected)
268 269
  val isReplayed = PopCount(io.deqResp.map(resp => resp.valid && !resp.bits.success))
  XSPerfAccumulate("replayed_entries", isReplayed)
270
}