StatusArray.scala 14.9 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
import xiangshan.mem.{SqPtr, MemWaitUpdateReq}
26

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 54
  val waitForSqIdx = new SqPtr // generated by store data valid check
  val waitForRobIdx = new RobPtr // generated by store set
55
  val waitForStoreData = Bool()
56
  val strictWait = Bool()
57
  val sqIdx = new SqPtr
58 59
  // misc
  val isFirstIssue = Bool()
60

61 62 63
  def canIssue: Bool = {
    val scheduledCond = if (params.needScheduledBit) !scheduled else true.B
    val blockedCond = if (params.checkWaitBit) !blocked else true.B
64 65 66 67 68 69 70 71
    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
72 73
  }

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

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

109
  val statusArray = Reg(Vec(params.numEntries, new StatusEntry(params)))
110 111 112 113 114 115 116
  val statusArrayNext = WireInit(statusArray)
  statusArray := statusArrayNext
  when (reset.asBool) {
    statusArray.map(_.valid := false.B)
  }

  // instruction is ready for issue
117 118
  val readyVec = VecInit(statusArray.map(_.canIssue))
  val readyVecNext = VecInit(statusArrayNext.map(_.canIssue))
119 120

  // update srcState when enqueue, wakeup
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
  // 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)
144
  }
145

146
  def deqRespSel(i: Int) : (Bool, Bool, UInt, SqPtr) = {
147 148
    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")
149 150 151 152 153
    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")
    }
154
    val successVec = io.deqResp.map(_.bits.success)
155
    val respTypeVec = io.deqResp.map(_.bits.resptype)
156 157
    val dataInvalidSqIdxVec = io.deqResp.map(_.bits.dataInvalidSqIdx)
    (mask.asUInt.orR, Mux1H(mask, successVec), Mux1H(mask, respTypeVec), Mux1H(mask, dataInvalidSqIdxVec))
158
  }
159 160 161 162 163 164 165 166

  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)
  }

167
  val flushedVec = Wire(Vec(params.numEntries, Bool()))
168 169 170 171

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

172
  val is_issued = Wire(Vec(params.numEntries, Bool()))
173
  for (((status, statusNext), i) <- statusArray.zip(statusArrayNext).zipWithIndex) {
174 175
    // valid: when the entry holds a valid instruction, mark it true.
    // Set when (1) not (flushed or deq); AND (2) update.
176
    val isFlushed = status.valid && status.robIdx.needFlush(io.redirect)
177
    val (deqRespValid, deqRespSucc, deqRespType, deqRespDataInvalidSqIdx) = deqResp(i)
178
    flushedVec(i) := isFlushed || (deqRespValid && deqRespSucc)
179
    val realUpdateValid = updateValid(i) && !io.redirect.valid
180 181
    statusNext.valid := !flushedVec(i) && (realUpdateValid || status.valid)
    XSError(updateValid(i) && status.valid, p"should not update a valid entry $i\n")
Y
temp  
Yinan Xu 已提交
182
    TimeOutAssert(status.valid, s"entry $i timeout\n", 40000)
183 184 185 186 187 188

    // 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
189
    statusNext.scheduled := false.B
190 191 192 193 194 195 196
    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")
197
    is_issued(i) := status.valid && hasIssued
198 199 200 201

    // blocked: indicate whether the entry is blocked for issue until certain conditions meet.
    statusNext.blocked := false.B
    if (params.checkWaitBit) {
202 203
      val blockNotReleased = isAfter(statusNext.sqIdx, io.stIssuePtr)
      val storeAddrWaitforIsIssuing = VecInit((0 until StorePipelineWidth).map(i => {
Y
temp  
Yinan Xu 已提交
204
        io.memWaitUpdateReq.staIssue(i).valid &&
205
        io.memWaitUpdateReq.staIssue(i).bits.uop.robIdx.value === statusNext.waitForRobIdx.value
206
      })).asUInt.orR && !statusNext.waitForStoreData && !statusNext.strictWait // is waiting for store addr ready
207
      val storeDataWaitforIsIssuing = VecInit((0 until StorePipelineWidth).map(i => {
Y
temp  
Yinan Xu 已提交
208
        io.memWaitUpdateReq.stdIssue(i).valid &&
209 210
        io.memWaitUpdateReq.stdIssue(i).bits.uop.sqIdx.value === statusNext.waitForSqIdx.value
      })).asUInt.orR && statusNext.waitForStoreData
Y
temp  
Yinan Xu 已提交
211
      statusNext.blocked := Mux(updateValid(i), updateVal(i).blocked, status.blocked) &&
212 213 214
        !storeAddrWaitforIsIssuing &&
        !storeDataWaitforIsIssuing &&
        blockNotReleased
215 216 217
      when(updateValid(i)) {
        statusNext.strictWait := updateVal(i).strictWait
        statusNext.waitForStoreData := updateVal(i).waitForStoreData
218
        statusNext.waitForRobIdx := updateVal(i).waitForRobIdx
219 220
        assert(updateVal(i).waitForStoreData === false.B)
      }
221 222
      when (deqNotGranted && deqRespType === RSFeedbackType.dataInvalid) {
        statusNext.blocked := true.B
223 224
        statusNext.waitForSqIdx := deqRespDataInvalidSqIdx
        statusNext.waitForStoreData := true.B
225 226
        XSError(status.valid && !isAfter(status.sqIdx, RegNext(RegNext(io.stIssuePtr))),
          "Previous store instructions are all issued. Should not trigger dataInvalid.\n")
227
      }
228 229
    }

230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
    // 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)
    })

248 249 250
    // midState: reset when enqueue; set when receiving feedback
    statusNext.midState := !updateValid(i) && (io.updateMidState(i) || status.midState)

251 252 253
    // 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 已提交
254
    statusNext.robIdx := Mux(updateValid(i), updateVal(i).robIdx, status.robIdx)
255 256 257
    statusNext.sqIdx := Mux(updateValid(i), updateVal(i).sqIdx, status.sqIdx)

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

261 262 263 264
    XSDebug(status.valid, p"entry[$i]: $status\n")
  }

  io.isValid := VecInit(statusArray.map(_.valid)).asUInt
265
  io.canIssue := VecInit(statusArrayNext.map(_.valid).zip(readyVecNext).map{ case (v, r) => v && r}).asUInt
266
  io.isFirstIssue := VecInit(io.issueGranted.map(iss => Mux1H(iss.bits, statusArray.map(_.isFirstIssue))))
267
  io.allSrcReady := VecInit(io.issueGranted.map(iss => Mux1H(iss.bits, statusArray.map(_.allSrcReady))))
268
  io.flushed := flushedVec.asUInt
269

270 271 272 273 274 275
  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))
276 277 278 279 280 281 282 283 284 285 286
    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))
      }
    }
287
  }
288 289
  val canIssueEntries = PopCount(io.canIssue)
  XSPerfHistogram("can_issue_entries", canIssueEntries, true.B, 0, params.numEntries, 1)
290 291 292 293
  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)
294
  val notSelected = RegNext(PopCount(io.canIssue)) - PopCount(is_issued)
295
  XSPerfAccumulate("not_selected_entries", notSelected)
296 297
  val isReplayed = PopCount(io.deqResp.map(resp => resp.valid && !resp.bits.success))
  XSPerfAccumulate("replayed_entries", isReplayed)
298
}