xref: /XiangShan/src/main/scala/xiangshan/mem/lsqueue/LoadQueueRAR.scala (revision c7353d05a480050d6a82606a7b3224bfda0f0438)
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 org.chipsalliance.cde.config._
21import xiangshan._
22import xiangshan.backend.rob.RobPtr
23import xiangshan.cache._
24import utils._
25import utility._
26import xiangshan.backend.Bundles.DynInst
27
28class LoadQueueRAR(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    val vecFeedback = Vec(VecLoadPipelineWidth, Flipped(ValidIO(new FeedbackToLsqIO)))
38
39    // violation query
40    val query = Vec(LoadPipelineWidth, Flipped(new LoadNukeQueryIO))
41
42    // release cacheline
43    val release = Flipped(Valid(new Release))
44
45    // from VirtualLoadQueue
46    val ldWbPtr = Input(new LqPtr)
47
48    // global
49    val lqFull = Output(Bool())
50  })
51
52  println("LoadQueueRAR: size: " + LoadQueueRARSize)
53  //  LoadQueueRAR field
54  //  +-------+-------+-------+----------+----+
55  //  | Valid |  Uop  | PAddr | Released | NC |
56  //  +-------+-------+-------+----------+----+
57  //
58  //  Field descriptions:
59  //  Allocated   : entry is valid.
60  //  MicroOp     : Micro-op
61  //  PAddr       : physical address.
62  //  Released    : DCache released.
63  //  NC          : is NC with data.
64  val allocated = RegInit(VecInit(List.fill(LoadQueueRARSize)(false.B))) // The control signals need to explicitly indicate the initial value
65  val uop = Reg(Vec(LoadQueueRARSize, new DynInst))
66  val paddrModule = Module(new LqPAddrModule(
67    gen = UInt(PAddrBits.W),
68    numEntries = LoadQueueRARSize,
69    numRead = LoadPipelineWidth,
70    numWrite = LoadPipelineWidth,
71    numWBank = LoadQueueNWriteBanks,
72    numWDelay = 2,
73    numCamPort = LoadPipelineWidth
74  ))
75  paddrModule.io := DontCare
76  val released = RegInit(VecInit(List.fill(LoadQueueRARSize)(false.B)))
77  val nc = RegInit(VecInit(List.fill(LoadQueueRARSize)(false.B)))
78  val bypassPAddr = Reg(Vec(LoadPipelineWidth, UInt(PAddrBits.W)))
79
80  // freeliset: store valid entries index.
81  // +---+---+--------------+-----+-----+
82  // | 0 | 1 |      ......  | n-2 | n-1 |
83  // +---+---+--------------+-----+-----+
84  val freeList = Module(new FreeList(
85    size = LoadQueueRARSize,
86    allocWidth = LoadPipelineWidth,
87    freeWidth = 4,
88    enablePreAlloc = true,
89    moduleName = "LoadQueueRAR freelist"
90  ))
91  freeList.io := DontCare
92
93  // Real-allocation: load_s2
94  // PAddr write needs 2 cycles, release signal should delay 1 cycle so that
95  // load enqueue can catch release.
96  val release1Cycle = io.release
97  // val release2Cycle = RegNext(io.release)
98  // val release2Cycle_dup_lsu = RegNext(io.release)
99  val release2Cycle = RegEnable(io.release, io.release.valid)
100  release2Cycle.valid := RegNext(io.release.valid)
101  //val release2Cycle_dup_lsu = RegEnable(io.release, io.release.valid)
102
103  // LoadQueueRAR enqueue condition:
104  // There are still not completed load instructions before the current load instruction.
105  // (e.g. "not completed" means that load instruction get the data or exception).
106  val canEnqueue = io.query.map(_.req.valid)
107  val cancelEnqueue = io.query.map(_.req.bits.uop.robIdx.needFlush(io.redirect))
108  val hasNotWritebackedLoad = io.query.map(_.req.bits.uop.lqIdx).map(lqIdx => isAfter(lqIdx, io.ldWbPtr))
109  val needEnqueue = canEnqueue.zip(hasNotWritebackedLoad).zip(cancelEnqueue).map { case ((v, r), c) => v && r && !c }
110
111  // Allocate logic
112  val acceptedVec = Wire(Vec(LoadPipelineWidth, Bool()))
113  val enqIndexVec = Wire(Vec(LoadPipelineWidth, UInt(log2Up(LoadQueueRARSize).W)))
114
115  for ((enq, w) <- io.query.map(_.req).zipWithIndex) {
116    acceptedVec(w) := false.B
117    paddrModule.io.wen(w) := false.B
118    freeList.io.doAllocate(w) := false.B
119
120    freeList.io.allocateReq(w) := true.B
121
122    //  Allocate ready
123    val offset = PopCount(needEnqueue.take(w))
124    val canAccept = freeList.io.canAllocate(offset)
125    val enqIndex = freeList.io.allocateSlot(offset)
126    enq.ready := Mux(needEnqueue(w), canAccept, true.B)
127
128    enqIndexVec(w) := enqIndex
129    when (needEnqueue(w) && enq.ready) {
130      acceptedVec(w) := true.B
131
132      val debug_robIdx = enq.bits.uop.robIdx.asUInt
133      XSError(allocated(enqIndex), p"LoadQueueRAR: You can not write an valid entry! check: ldu $w, robIdx $debug_robIdx")
134
135      freeList.io.doAllocate(w) := true.B
136      //  Allocate new entry
137      allocated(enqIndex) := true.B
138
139      //  Write paddr
140      paddrModule.io.wen(w) := true.B
141      paddrModule.io.waddr(w) := enqIndex
142      paddrModule.io.wdata(w) := enq.bits.paddr
143      bypassPAddr(w) := enq.bits.paddr
144
145      //  Fill info
146      uop(enqIndex) := enq.bits.uop
147      released(enqIndex) :=
148        enq.bits.data_valid &&
149        (release2Cycle.valid &&
150        enq.bits.paddr(PAddrBits-1, DCacheLineOffset) === release2Cycle.bits.paddr(PAddrBits-1, DCacheLineOffset) ||
151        release1Cycle.valid &&
152        enq.bits.paddr(PAddrBits-1, DCacheLineOffset) === release1Cycle.bits.paddr(PAddrBits-1, DCacheLineOffset))
153      nc(enqIndex) := enq.bits.is_nc
154    }
155  }
156
157  //  LoadQueueRAR deallocate
158  val freeMaskVec = Wire(Vec(LoadQueueRARSize, Bool()))
159
160  // init
161  freeMaskVec.map(e => e := false.B)
162
163  // when the loads that "older than" current load were writebacked,
164  // current load will be released.
165  val vecLdCanceltmp = Wire(Vec(LoadQueueRARSize, Vec(VecLoadPipelineWidth, Bool())))
166  val vecLdCancel = Wire(Vec(LoadQueueRARSize, Bool()))
167  for (i <- 0 until LoadQueueRARSize) {
168    val deqNotBlock = !isBefore(io.ldWbPtr, uop(i).lqIdx)
169    val needFlush = uop(i).robIdx.needFlush(io.redirect)
170    val fbk = io.vecFeedback
171    for (j <- 0 until VecLoadPipelineWidth) {
172      vecLdCanceltmp(i)(j) := allocated(i) && fbk(j).valid && fbk(j).bits.isFlush && uop(i).robIdx === fbk(j).bits.robidx && uop(i).uopIdx === fbk(j).bits.uopidx
173    }
174    vecLdCancel(i) := vecLdCanceltmp(i).reduce(_ || _)
175
176    when (allocated(i) && (deqNotBlock || needFlush || vecLdCancel(i))) {
177      allocated(i) := false.B
178      freeMaskVec(i) := true.B
179    }
180  }
181
182  // if need replay revoke entry
183  val lastCanAccept = GatedRegNext(acceptedVec)
184  val lastAllocIndex = GatedRegNext(enqIndexVec)
185
186  for ((revoke, w) <- io.query.map(_.revoke).zipWithIndex) {
187    val revokeValid = revoke && lastCanAccept(w)
188    val revokeIndex = lastAllocIndex(w)
189
190    when (allocated(revokeIndex) && revokeValid) {
191      allocated(revokeIndex) := false.B
192      freeMaskVec(revokeIndex) := true.B
193    }
194  }
195
196  freeList.io.free := freeMaskVec.asUInt
197
198  // LoadQueueRAR Query
199  // Load-to-Load violation check condition:
200  // 1. Physical address match by CAM port.
201  // 2. release or nc_with_data is set.
202  // 3. Younger than current load instruction.
203  val ldLdViolation = Wire(Vec(LoadPipelineWidth, Bool()))
204  //val allocatedUInt = RegNext(allocated.asUInt)
205  for ((query, w) <- io.query.zipWithIndex) {
206    ldLdViolation(w) := false.B
207    paddrModule.io.releaseViolationMdata(w) := query.req.bits.paddr
208
209    query.resp.valid := RegNext(query.req.valid)
210    // Generate real violation mask
211    val robIdxMask = VecInit(uop.map(_.robIdx).map(isAfter(_, query.req.bits.uop.robIdx)))
212    val matchMaskReg = Wire(Vec(LoadQueueRARSize, Bool()))
213    for(i <- 0 until LoadQueueRARSize) {
214      matchMaskReg(i) := (allocated(i) &
215                         paddrModule.io.releaseViolationMmask(w)(i) &
216                         robIdxMask(i) &&
217                         (nc(i) || released(i)))
218      }
219    val matchMask = GatedValidRegNext(matchMaskReg)
220    //  Load-to-Load violation check result
221    val ldLdViolationMask = matchMask
222    ldLdViolationMask.suggestName("ldLdViolationMask_" + w)
223    query.resp.bits.rep_frm_fetch := ParallelORR(ldLdViolationMask)
224  }
225
226
227  // When io.release.valid (release1cycle.valid), it uses the last ld-ld paddr cam port to
228  // update release flag in 1 cycle
229  val releaseVioMask = Reg(Vec(LoadQueueRARSize, Bool()))
230  when (release1Cycle.valid) {
231    paddrModule.io.releaseMdata.takeRight(1)(0) := release1Cycle.bits.paddr
232  }
233
234  val lastAllocIndexOH = lastAllocIndex.map(UIntToOH(_))
235  val lastReleasePAddrMatch = VecInit((0 until LoadPipelineWidth).map(i => {
236    (bypassPAddr(i)(PAddrBits-1, DCacheLineOffset) === release1Cycle.bits.paddr(PAddrBits-1, DCacheLineOffset))
237  }))
238  (0 until LoadQueueRARSize).map(i => {
239    val bypassMatch = VecInit((0 until LoadPipelineWidth).map(j => lastCanAccept(j) && lastAllocIndexOH(j)(i) && lastReleasePAddrMatch(j))).asUInt.orR
240    when (RegNext((paddrModule.io.releaseMmask.takeRight(1)(0)(i) || bypassMatch) && allocated(i) && release1Cycle.valid)) {
241      // Note: if a load has missed in dcache and is waiting for refill in load queue,
242      // its released flag still needs to be set as true if addr matches.
243      released(i) := true.B
244    }
245  })
246
247  io.lqFull := freeList.io.empty
248
249  // perf cnt
250  val canEnqCount = PopCount(io.query.map(_.req.fire))
251  val validCount = freeList.io.validCount
252  val allowEnqueue = validCount <= (LoadQueueRARSize - LoadPipelineWidth).U
253  val ldLdViolationCount = PopCount(io.query.map(_.resp).map(resp => resp.valid && resp.bits.rep_frm_fetch))
254
255  QueuePerf(LoadQueueRARSize, validCount, !allowEnqueue)
256  XSPerfAccumulate("enq", canEnqCount)
257  XSPerfAccumulate("ld_ld_violation", ldLdViolationCount)
258  val perfEvents: Seq[(String, UInt)] = Seq(
259    ("enq", canEnqCount),
260    ("ld_ld_violation", ldLdViolationCount)
261  )
262  generatePerfEvent()
263  // End
264}