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