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