xref: /XiangShan/src/main/scala/xiangshan/mem/lsqueue/VirtualLoadQueue.scala (revision d2b20d1a96e238e36a849bd253f65ec7b6a5db38)
1/***************************************************************************************
2* Copyright (c) 2020-2021 Institute of Computing Technology, Chinese Academy of Sciences
3* Copyright (c) 2020-2021 Peng Cheng Laboratory
4*
5* XiangShan is licensed under Mulan PSL v2.
6* You can use this software according to the terms and conditions of the Mulan PSL v2.
7* You may obtain a copy of Mulan PSL v2 at:
8*          http://license.coscl.org.cn/MulanPSL2
9*
10* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
11* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
12* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
13*
14* See the Mulan PSL v2 for more details.
15***************************************************************************************/
16package xiangshan.mem
17
18import chisel3._
19import chisel3.util._
20import chipsalliance.rocketchip.config._
21import xiangshan._
22import xiangshan.backend.rob.{RobPtr, RobLsqIO}
23import xiangshan.ExceptionNO._
24import xiangshan.cache._
25import utils._
26import utility._
27
28class VirtualLoadQueue(implicit p: Parameters) extends XSModule
29  with HasDCacheParameters
30  with HasCircularQueuePtrHelper
31  with HasLoadHelper
32  with HasPerfEvents
33{
34  val io = IO(new Bundle() {
35    val redirect = Flipped(Valid(new Redirect))
36    val enq = new LqEnqIO
37    val loadIn = Vec(LoadPipelineWidth, Flipped(DecoupledIO(new LqWriteBundle)))
38    val ldWbPtr = Output(new LqPtr)
39    val lqFull = Output(Bool())
40    val lqDeq = Output(UInt(log2Up(CommitWidth + 1).W))
41    val lqCancelCnt = Output(UInt(log2Up(VirtualLoadQueueSize+1).W))
42  })
43
44  println("VirtualLoadQueue: size: " + VirtualLoadQueueSize)
45  //  VirtualLoadQueue field
46  //  +-----------+---------+-------+
47  //  | Allocated | MicroOp | Flags |
48  //  +-----------+---------+-------+
49  //  Allocated   : entry has been allocated already
50  //  MicroOp     : inst's microOp
51  //  Flags       : load flags
52  val allocated = RegInit(VecInit(List.fill(VirtualLoadQueueSize)(false.B))) // The control signals need to explicitly indicate the initial value
53  val uop = Reg(Vec(VirtualLoadQueueSize, new MicroOp))
54  val addrvalid = RegInit(VecInit(List.fill(VirtualLoadQueueSize)(false.B))) // non-mmio addr is valid
55  val datavalid = RegInit(VecInit(List.fill(VirtualLoadQueueSize)(false.B))) // non-mmio data is valid
56
57  /**
58   * used for debug
59   */
60  val debug_mmio = Reg(Vec(VirtualLoadQueueSize, Bool())) // mmio: inst is an mmio inst
61  val debug_paddr = Reg(Vec(VirtualLoadQueueSize, UInt(PAddrBits.W))) // mmio: inst's paddr
62
63  //  maintain pointers
64  val enqPtrExt = RegInit(VecInit((0 until io.enq.req.length).map(_.U.asTypeOf(new LqPtr))))
65  val enqPtr = enqPtrExt(0).value
66  val deqPtr = Wire(new LqPtr)
67  val deqPtrNext = Wire(new LqPtr)
68
69  /**
70   * update pointer
71   */
72  val lastCycleRedirect = RegNext(io.redirect)
73  val lastLastCycleRedirect = RegNext(lastCycleRedirect)
74
75  val validCount = distanceBetween(enqPtrExt(0), deqPtr)
76  val allowEnqueue = validCount <= (VirtualLoadQueueSize - LoadPipelineWidth).U
77  val canEnqueue = io.enq.req.map(_.valid)
78  val needCancel = WireInit(VecInit((0 until VirtualLoadQueueSize).map(i => {
79    uop(i).robIdx.needFlush(io.redirect) && allocated(i)
80  })))
81  val lastNeedCancel = RegNext(needCancel)
82  val enqCancel = io.enq.req.map(_.bits.robIdx.needFlush(io.redirect))
83  val lastEnqCancel = PopCount(RegNext(VecInit(canEnqueue.zip(enqCancel).map(x => x._1 && x._2))))
84  val lastCycleCancelCount = PopCount(lastNeedCancel)
85
86  // update enqueue pointer
87  val enqCount = Mux(io.enq.canAccept && io.enq.sqCanAccept, PopCount(io.enq.req.map(_.valid)), 0.U)
88  val enqPtrExtNextVec = Wire(Vec(io.enq.req.length, new LqPtr))
89  val enqPtrExtNext = Wire(Vec(io.enq.req.length, new LqPtr))
90  when (lastCycleRedirect.valid) {
91    // we recover the pointers in the next cycle after redirect
92    enqPtrExtNextVec := VecInit(enqPtrExt.map(_ - (lastCycleCancelCount + lastEnqCancel)))
93  }.otherwise {
94    enqPtrExtNextVec := VecInit(enqPtrExt.map(_ + enqCount))
95  }
96
97  when (isAfter(enqPtrExtNextVec(0), deqPtrNext)) {
98    enqPtrExtNext := enqPtrExtNextVec
99  } .otherwise {
100    enqPtrExtNext := VecInit((0 until io.enq.req.length).map(i => deqPtrNext + i.U))
101  }
102  enqPtrExt := enqPtrExtNext
103
104  // update dequeue pointer
105  val DeqPtrMoveStride = CommitWidth
106  require(DeqPtrMoveStride == CommitWidth, "DeqPtrMoveStride must be equal to CommitWidth!")
107  val deqLookupVec = VecInit((0 until DeqPtrMoveStride).map(deqPtr + _.U))
108  val deqLookup = VecInit(deqLookupVec.map(ptr => allocated(ptr.value) && datavalid(ptr.value) && addrvalid(ptr.value) && ptr =/= enqPtrExt(0)))
109  val deqInSameRedirectCycle = VecInit(deqLookupVec.map(ptr => needCancel(ptr.value)))
110  // make chisel happy
111  val deqCountMask = Wire(UInt(DeqPtrMoveStride.W))
112  deqCountMask := deqLookup.asUInt & ~deqInSameRedirectCycle.asUInt
113  val commitCount = PopCount(PriorityEncoderOH(~deqCountMask) - 1.U)
114  val lastCommitCount = RegNext(commitCount)
115
116  // update deqPtr
117  // cycle 1: generate deqPtrNext
118  // cycle 2: update deqPtr
119  val deqPtrUpdateEna = lastCommitCount =/= 0.U
120  deqPtrNext := deqPtr + lastCommitCount
121  deqPtr := RegEnable(next = deqPtrNext, init = 0.U.asTypeOf(new LqPtr), enable = deqPtrUpdateEna)
122
123  io.lqDeq := RegNext(lastCommitCount)
124  io.lqCancelCnt := RegNext(lastCycleCancelCount + lastEnqCancel)
125  io.ldWbPtr := deqPtr
126
127  /**
128   * Enqueue at dispatch
129   *
130   * Currently, VirtualLoadQueue only allows enqueue when #emptyEntries > EnqWidth
131   */
132  io.enq.canAccept := allowEnqueue
133  for (i <- 0 until io.enq.req.length) {
134    val offset = PopCount(io.enq.needAlloc.take(i))
135    val lqIdx = enqPtrExt(offset)
136    val index = io.enq.req(i).bits.lqIdx.value
137    when (canEnqueue(i) && !enqCancel(i)) {
138      allocated(index) := true.B
139      uop(index) := io.enq.req(i).bits
140      uop(index).lqIdx := lqIdx
141
142      // init
143      addrvalid(index) := false.B
144      datavalid(index) := false.B
145
146      debug_mmio(index) := false.B
147      debug_paddr(index) := 0.U
148
149      XSError(!io.enq.canAccept || !io.enq.sqCanAccept, s"must accept $i\n")
150      XSError(index =/= lqIdx.value, s"must be the same entry $i\n")
151    }
152    io.enq.resp(i) := lqIdx
153  }
154
155  /**
156    * Load commits
157    *
158    * When load commited, mark it as !allocated and move deqPtr forward.
159    */
160  (0 until DeqPtrMoveStride).map(i => {
161    when (commitCount > i.U) {
162      allocated((deqPtr+i.U).value) := false.B
163      XSError(!allocated((deqPtr+i.U).value), s"why commit invalid entry $i?\n")
164    }
165  })
166
167  // misprediction recovery / exception redirect
168  // invalidate lq term using robIdx
169  for (i <- 0 until VirtualLoadQueueSize) {
170    when (needCancel(i)) {
171      allocated(i) := false.B
172    }
173  }
174
175  XSDebug(p"(ready, valid): ${io.enq.canAccept}, ${Binary(Cat(io.enq.req.map(_.valid)))}\n")
176
177  /**
178    * Writeback load from load units
179    *
180    * Most load instructions writeback to regfile at the same time.
181    * However,
182    *   (1) For ready load instruction (no need replay), it writes back to ROB immediately.
183    */
184  for(i <- 0 until LoadPipelineWidth) {
185    //   most lq status need to be updated immediately after load writeback to lq
186    //   flag bits in lq needs to be updated accurately
187    io.loadIn(i).ready := true.B
188    val loadWbIndex = io.loadIn(i).bits.uop.lqIdx.value
189
190    when (io.loadIn(i).valid) {
191      val hasExceptions = ExceptionNO.selectByFu(io.loadIn(i).bits.uop.cf.exceptionVec, lduCfg).asUInt.orR
192      val needReplay = io.loadIn(i).bits.replayInfo.needReplay()
193
194      when (!needReplay) {
195      // update control flag
196        addrvalid(loadWbIndex) := hasExceptions || !io.loadIn(i).bits.tlbMiss
197        datavalid(loadWbIndex) :=
198          (if (EnableFastForward) {
199              hasExceptions ||
200              io.loadIn(i).bits.mmio ||
201             !io.loadIn(i).bits.miss && // dcache miss
202             !io.loadIn(i).bits.dcacheRequireReplay // do not writeback if that inst will be resend from rs
203           } else {
204              hasExceptions ||
205              io.loadIn(i).bits.mmio ||
206             !io.loadIn(i).bits.miss
207           })
208
209        //
210        when (io.loadIn(i).bits.lqDataWenDup(1)) {
211          uop(loadWbIndex).pdest := io.loadIn(i).bits.uop.pdest
212        }
213        when (io.loadIn(i).bits.lqDataWenDup(2)) {
214          uop(loadWbIndex).cf := io.loadIn(i).bits.uop.cf
215        }
216        when (io.loadIn(i).bits.lqDataWenDup(3)) {
217          uop(loadWbIndex).ctrl := io.loadIn(i).bits.uop.ctrl
218        }
219        when (io.loadIn(i).bits.lqDataWenDup(4)) {
220          uop(loadWbIndex).debugInfo := io.loadIn(i).bits.uop.debugInfo
221        }
222        uop(loadWbIndex).debugInfo := io.loadIn(i).bits.replayInfo.debug
223
224        //  Debug info
225        debug_mmio(loadWbIndex) := io.loadIn(i).bits.mmio
226        debug_paddr(loadWbIndex) := io.loadIn(i).bits.paddr
227
228        XSInfo(io.loadIn(i).valid, "load hit write to lq idx %d pc 0x%x vaddr %x paddr %x mask %x forwardData %x forwardMask: %x mmio %x\n",
229          io.loadIn(i).bits.uop.lqIdx.asUInt,
230          io.loadIn(i).bits.uop.cf.pc,
231          io.loadIn(i).bits.vaddr,
232          io.loadIn(i).bits.paddr,
233          io.loadIn(i).bits.mask,
234          io.loadIn(i).bits.forwardData.asUInt,
235          io.loadIn(i).bits.forwardMask.asUInt,
236          io.loadIn(i).bits.mmio
237        )
238      }
239    }
240  }
241
242  //  perf counter
243  QueuePerf(VirtualLoadQueueSize, validCount, !allowEnqueue)
244  io.lqFull := !allowEnqueue
245  val perfEvents: Seq[(String, UInt)] = Seq()
246  generatePerfEvent()
247
248  // debug info
249  XSDebug("enqPtrExt %d:%d deqPtrExt %d:%d\n", enqPtrExt(0).flag, enqPtr, deqPtr.flag, deqPtr.value)
250
251  def PrintFlag(flag: Bool, name: String): Unit = {
252    when(flag) {
253      XSDebug(false, true.B, name)
254    }.otherwise {
255      XSDebug(false, true.B, " ")
256    }
257  }
258
259  for (i <- 0 until VirtualLoadQueueSize) {
260    XSDebug(i + " pc %x pa %x ", uop(i).cf.pc, debug_paddr(i))
261    PrintFlag(allocated(i), "v")
262    PrintFlag(allocated(i) && datavalid(i), "d")
263    PrintFlag(allocated(i) && addrvalid(i), "a")
264    PrintFlag(allocated(i) && addrvalid(i) && datavalid(i), "w")
265    XSDebug(false, true.B, "\n")
266  }
267  // end
268}
269