xref: /XiangShan/src/main/scala/xiangshan/mem/lsqueue/VirtualLoadQueue.scala (revision 1b46b9591920008655d659ac88cd0250db769664)
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    // control
36    val redirect    = Flipped(Valid(new Redirect))
37    // from dispatch
38    val enq         = new LqEnqIO
39    // from ldu s3
40    val ldin        = Vec(LoadPipelineWidth, Flipped(DecoupledIO(new LqWriteBundle)))
41    // to LoadQueueReplay and LoadQueueRAR
42    val ldWbPtr     = Output(new LqPtr)
43    // global
44    val lqFull      = Output(Bool())
45    // to dispatch
46    val lqDeq       = Output(UInt(log2Up(CommitWidth + 1).W))
47    val lqCancelCnt = Output(UInt(log2Up(VirtualLoadQueueSize+1).W))
48  })
49
50  println("VirtualLoadQueue: size: " + VirtualLoadQueueSize)
51  //  VirtualLoadQueue field
52  //  +-----------+---------+-------+
53  //  | Allocated | MicroOp | Flags |
54  //  +-----------+---------+-------+
55  //  Allocated   : entry has been allocated already
56  //  MicroOp     : inst's microOp
57  //  Flags       : load flags
58  val allocated = RegInit(VecInit(List.fill(VirtualLoadQueueSize)(false.B))) // The control signals need to explicitly indicate the initial value
59  val uop = Reg(Vec(VirtualLoadQueueSize, new MicroOp))
60  val addrvalid = RegInit(VecInit(List.fill(VirtualLoadQueueSize)(false.B))) // non-mmio addr is valid
61  val datavalid = RegInit(VecInit(List.fill(VirtualLoadQueueSize)(false.B))) // non-mmio data is valid
62
63  /**
64   * used for debug
65   */
66  val debug_mmio = Reg(Vec(VirtualLoadQueueSize, Bool())) // mmio: inst is an mmio inst
67  val debug_paddr = Reg(Vec(VirtualLoadQueueSize, UInt(PAddrBits.W))) // mmio: inst's paddr
68
69  //  maintain pointers
70  val enqPtrExt = RegInit(VecInit((0 until io.enq.req.length).map(_.U.asTypeOf(new LqPtr))))
71  val enqPtr = enqPtrExt(0).value
72  val deqPtr = Wire(new LqPtr)
73  val deqPtrNext = Wire(new LqPtr)
74
75  /**
76   * update pointer
77   */
78  val lastCycleRedirect = RegNext(io.redirect)
79  val lastLastCycleRedirect = RegNext(lastCycleRedirect)
80
81  val validCount = distanceBetween(enqPtrExt(0), deqPtr)
82  val allowEnqueue = validCount <= (VirtualLoadQueueSize - LoadPipelineWidth).U
83  val canEnqueue = io.enq.req.map(_.valid)
84  val needCancel = WireInit(VecInit((0 until VirtualLoadQueueSize).map(i => {
85    uop(i).robIdx.needFlush(io.redirect) && allocated(i)
86  })))
87  val lastNeedCancel = RegNext(needCancel)
88  val enqCancel = io.enq.req.map(_.bits.robIdx.needFlush(io.redirect))
89  val lastEnqCancel = PopCount(RegNext(VecInit(canEnqueue.zip(enqCancel).map(x => x._1 && x._2))))
90  val lastCycleCancelCount = PopCount(lastNeedCancel)
91  val redirectCancelCount = RegEnable(next = lastCycleCancelCount + lastEnqCancel, init = 0.U, enable = lastCycleRedirect.valid)
92
93  // update enqueue pointer
94  val enqNumber = Mux(io.enq.canAccept && io.enq.sqCanAccept, PopCount(io.enq.req.map(_.valid)), 0.U)
95  val enqPtrExtNextVec = Wire(Vec(io.enq.req.length, new LqPtr))
96  val enqPtrExtNext = Wire(Vec(io.enq.req.length, new LqPtr))
97  when (lastLastCycleRedirect.valid) {
98    // we recover the pointers in the next cycle after redirect
99    enqPtrExtNextVec := VecInit(enqPtrExt.map(_ - redirectCancelCount))
100  } .otherwise {
101    enqPtrExtNextVec := VecInit(enqPtrExt.map(_ + enqNumber))
102  }
103  assert(!(lastCycleRedirect.valid && enqNumber =/= 0.U))
104
105  when (isAfter(enqPtrExtNextVec(0), deqPtrNext)) {
106    enqPtrExtNext := enqPtrExtNextVec
107  } .otherwise {
108    enqPtrExtNext := VecInit((0 until io.enq.req.length).map(i => deqPtrNext + i.U))
109  }
110  enqPtrExt := enqPtrExtNext
111
112  // update dequeue pointer
113  val DeqPtrMoveStride = CommitWidth
114  require(DeqPtrMoveStride == CommitWidth, "DeqPtrMoveStride must be equal to CommitWidth!")
115  val deqLookupVec = VecInit((0 until DeqPtrMoveStride).map(deqPtr + _.U))
116  val deqLookup = VecInit(deqLookupVec.map(ptr => allocated(ptr.value) && datavalid(ptr.value) && addrvalid(ptr.value) && ptr =/= enqPtrExt(0)))
117  val deqInSameRedirectCycle = VecInit(deqLookupVec.map(ptr => needCancel(ptr.value)))
118  // make chisel happy
119  val deqCountMask = Wire(UInt(DeqPtrMoveStride.W))
120  deqCountMask := deqLookup.asUInt & ~deqInSameRedirectCycle.asUInt
121  val commitCount = PopCount(PriorityEncoderOH(~deqCountMask) - 1.U)
122  val lastCommitCount = RegNext(commitCount)
123
124  // update deqPtr
125  // cycle 1: generate deqPtrNext
126  // cycle 2: update deqPtr
127  val deqPtrUpdateEna = lastCommitCount =/= 0.U
128  deqPtrNext := deqPtr + lastCommitCount
129  deqPtr := RegEnable(next = deqPtrNext, init = 0.U.asTypeOf(new LqPtr), enable = deqPtrUpdateEna)
130
131  io.lqDeq := RegNext(lastCommitCount)
132  io.lqCancelCnt := redirectCancelCount
133  io.ldWbPtr := deqPtr
134
135  /**
136   * Enqueue at dispatch
137   *
138   * Currently, VirtualLoadQueue only allows enqueue when #emptyEntries > EnqWidth
139   */
140  io.enq.canAccept := allowEnqueue
141  for (i <- 0 until io.enq.req.length) {
142    val offset = PopCount(io.enq.needAlloc.take(i))
143    val lqIdx = enqPtrExt(offset)
144    val index = io.enq.req(i).bits.lqIdx.value
145    when (canEnqueue(i) && !enqCancel(i)) {
146      allocated(index) := true.B
147      uop(index) := io.enq.req(i).bits
148      uop(index).lqIdx := lqIdx
149
150      // init
151      addrvalid(index) := false.B
152      datavalid(index) := false.B
153
154      debug_mmio(index) := false.B
155      debug_paddr(index) := 0.U
156
157      XSError(!io.enq.canAccept || !io.enq.sqCanAccept, s"must accept $i\n")
158      XSError(index =/= lqIdx.value, s"must be the same entry $i\n")
159    }
160    io.enq.resp(i) := lqIdx
161  }
162
163  /**
164    * Load commits
165    *
166    * When load commited, mark it as !allocated and move deqPtr forward.
167    */
168  (0 until DeqPtrMoveStride).map(i => {
169    when (commitCount > i.U) {
170      allocated((deqPtr+i.U).value) := false.B
171      XSError(!allocated((deqPtr+i.U).value), s"why commit invalid entry $i?\n")
172    }
173  })
174
175  // misprediction recovery / exception redirect
176  // invalidate lq term using robIdx
177  for (i <- 0 until VirtualLoadQueueSize) {
178    when (needCancel(i)) {
179      allocated(i) := false.B
180    }
181  }
182
183  XSDebug(p"(ready, valid): ${io.enq.canAccept}, ${Binary(Cat(io.enq.req.map(_.valid)))}\n")
184
185  /**
186    * Writeback load from load units
187    *
188    * Most load instructions writeback to regfile at the same time.
189    * However,
190    *   (1) For ready load instruction (no need replay), it writes back to ROB immediately.
191    */
192  for(i <- 0 until LoadPipelineWidth) {
193    //   most lq status need to be updated immediately after load writeback to lq
194    //   flag bits in lq needs to be updated accurately
195    io.ldin(i).ready := true.B
196    val loadWbIndex = io.ldin(i).bits.uop.lqIdx.value
197
198    when (io.ldin(i).valid) {
199      val hasExceptions = ExceptionNO.selectByFu(io.ldin(i).bits.uop.cf.exceptionVec, lduCfg).asUInt.orR
200      val need_rep = io.ldin(i).bits.rep_info.need_rep
201
202      when (!need_rep) {
203      // update control flag
204        addrvalid(loadWbIndex) := hasExceptions || !io.ldin(i).bits.tlbMiss
205        datavalid(loadWbIndex) :=
206          (if (EnableFastForward) {
207              hasExceptions ||
208              io.ldin(i).bits.mmio ||
209             !io.ldin(i).bits.miss && // dcache miss
210             !io.ldin(i).bits.dcacheRequireReplay // do not writeback if that inst will be resend from rs
211           } else {
212              hasExceptions ||
213              io.ldin(i).bits.mmio ||
214             !io.ldin(i).bits.miss
215           })
216
217        //
218        when (io.ldin(i).bits.data_wen_dup(1)) {
219          uop(loadWbIndex).pdest := io.ldin(i).bits.uop.pdest
220        }
221        when (io.ldin(i).bits.data_wen_dup(2)) {
222          uop(loadWbIndex).cf := io.ldin(i).bits.uop.cf
223        }
224        when (io.ldin(i).bits.data_wen_dup(3)) {
225          uop(loadWbIndex).ctrl := io.ldin(i).bits.uop.ctrl
226        }
227        when (io.ldin(i).bits.data_wen_dup(4)) {
228          uop(loadWbIndex).debugInfo := io.ldin(i).bits.uop.debugInfo
229        }
230        uop(loadWbIndex).debugInfo := io.ldin(i).bits.rep_info.debug
231
232        //  Debug info
233        debug_mmio(loadWbIndex) := io.ldin(i).bits.mmio
234        debug_paddr(loadWbIndex) := io.ldin(i).bits.paddr
235
236        XSInfo(io.ldin(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",
237          io.ldin(i).bits.uop.lqIdx.asUInt,
238          io.ldin(i).bits.uop.cf.pc,
239          io.ldin(i).bits.vaddr,
240          io.ldin(i).bits.paddr,
241          io.ldin(i).bits.mask,
242          io.ldin(i).bits.forwardData.asUInt,
243          io.ldin(i).bits.forwardMask.asUInt,
244          io.ldin(i).bits.mmio
245        )
246      }
247    }
248  }
249
250  //  perf counter
251  QueuePerf(VirtualLoadQueueSize, validCount, !allowEnqueue)
252  io.lqFull := !allowEnqueue
253  val perfEvents: Seq[(String, UInt)] = Seq()
254  generatePerfEvent()
255
256  // debug info
257  XSDebug("enqPtrExt %d:%d deqPtrExt %d:%d\n", enqPtrExt(0).flag, enqPtr, deqPtr.flag, deqPtr.value)
258
259  def PrintFlag(flag: Bool, name: String): Unit = {
260    when(flag) {
261      XSDebug(false, true.B, name)
262    }.otherwise {
263      XSDebug(false, true.B, " ")
264    }
265  }
266
267  for (i <- 0 until VirtualLoadQueueSize) {
268    XSDebug(i + " pc %x pa %x ", uop(i).cf.pc, debug_paddr(i))
269    PrintFlag(allocated(i), "v")
270    PrintFlag(allocated(i) && datavalid(i), "d")
271    PrintFlag(allocated(i) && addrvalid(i), "a")
272    PrintFlag(allocated(i) && addrvalid(i) && datavalid(i), "w")
273    XSDebug(false, true.B, "\n")
274  }
275  // end
276}
277