xref: /XiangShan/src/main/scala/xiangshan/mem/mdp/StoreSet.scala (revision 8b33cd30e0034914b58520e0dc3c0c4b1aad6a03)
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*
16*
17* Acknowledgement
18*
19* This implementation is inspired by several key papers:
20* [1] George Z. Chrysos, and Joel S. Emer. "[Memory dependence prediction using store sets.]
21* (https://doi.org/10.1109/ISCA.1998.694770)" 25th Annual International Symposium on Computer Architecture (ISCA).
22* 1998.
23***************************************************************************************/
24
25package xiangshan.mem.mdp
26
27import org.chipsalliance.cde.config.Parameters
28import chisel3._
29import chisel3.util._
30import xiangshan._
31import utils._
32import utility._
33import xiangshan.backend.rob.RobPtr
34import xiangshan.backend.Bundles.DynInst
35
36// store set load violation predictor
37// See "Memory Dependence Prediction using Store Sets" for details
38
39// Store Set Identifier Table Entry
40class SSITEntry(implicit p: Parameters) extends XSBundle {
41  val valid = Bool()
42  val ssid = UInt(SSIDWidth.W) // store set identifier
43  val strict = Bool() // strict load wait is needed
44}
45
46// Store Set Identifier Table Entry
47class SSITDataEntry(implicit p: Parameters) extends XSBundle {
48  val ssid = UInt(SSIDWidth.W) // store set identifier
49  val strict = Bool() // strict load wait is needed
50}
51
52// Store Set Identifier Table
53class SSIT(implicit p: Parameters) extends XSModule {
54  val io = IO(new Bundle {
55    // to decode
56    val ren = Vec(DecodeWidth, Input(Bool()))
57    val raddr = Vec(DecodeWidth, Input(UInt(MemPredPCWidth.W))) // xor hashed decode pc(VaddrBits-1, 1)
58    // to rename
59    val rdata = Vec(RenameWidth, Output(new SSITEntry))
60    // misc
61    val update = Input(new MemPredUpdateReq) // RegNext should be added outside
62    val csrCtrl = Input(new CustomCSRCtrlIO)
63  })
64
65  // raddrs are sent to ssit in decode
66  // rdata will be send to rename
67  require(DecodeWidth == RenameWidth)
68
69  // data sram read port allocate
70  //
71  // SSIT update logic will reuse decode ssit read port.
72  // If io.update.valid, a redirect will be send to frontend,
73  // then decode will not need to read SSIT
74  val SSIT_DECODE_READ_PORT_BASE = 0
75  val SSIT_UPDATE_LOAD_READ_PORT = 0
76  val SSIT_UPDATE_STORE_READ_PORT = 1
77  val SSIT_READ_PORT_NUM = DecodeWidth
78
79  // data sram write port allocate
80  // load update and flush uses the same write port
81  val SSIT_MISC_WRITE_PORT = 0
82  val SSIT_UPDATE_LOAD_WRITE_PORT = 0
83  val SSIT_UPDATE_STORE_WRITE_PORT = 1
84  val SSIT_WRITE_PORT_NUM = 2
85
86  private def hasRen: Boolean = true
87  val valid_array = Module(new SyncDataModuleTemplate(
88    Bool(),
89    SSITSize,
90    SSIT_READ_PORT_NUM,
91    SSIT_WRITE_PORT_NUM,
92    hasRen = hasRen,
93  ))
94
95  val data_array = Module(new SyncDataModuleTemplate(
96    new SSITDataEntry,
97    SSITSize,
98    SSIT_READ_PORT_NUM,
99    SSIT_WRITE_PORT_NUM,
100    hasRen = hasRen,
101  ))
102
103  // TODO: use SRAM or not?
104  (0 until SSIT_WRITE_PORT_NUM).map(i => {
105    valid_array.io.wen(i) := false.B
106    valid_array.io.waddr(i) := 0.U
107    valid_array.io.wdata(i) := false.B
108    data_array.io.wen(i) := false.B
109    data_array.io.waddr(i) := 0.U
110    data_array.io.wdata(i) := 0.U.asTypeOf(new SSITDataEntry)
111  })
112
113  val debug_valid = RegInit(VecInit(Seq.fill(SSITSize)(false.B)))
114  val debug_ssid = Reg(Vec(SSITSize, UInt(SSIDWidth.W)))
115  val debug_strict = Reg(Vec(SSITSize, Bool()))
116  if(!env.FPGAPlatform){
117    dontTouch(debug_valid)
118    dontTouch(debug_ssid)
119    dontTouch(debug_strict)
120  }
121
122  val resetCounter = RegInit(0.U(ResetTimeMax2Pow.W))
123  resetCounter := resetCounter + 1.U
124
125  for (i <- 0 until DecodeWidth) {
126    // io.rdata(i).valid := RegNext(valid(io.raddr(i)))
127    // io.rdata(i).ssid := RegNext(ssid(io.raddr(i)))
128    // io.rdata(i).strict := RegNext(strict(io.raddr(i)) && valid(io.raddr(i)))
129
130    // read SSIT in decode stage
131    valid_array.io.ren.get(i) := io.ren(i)
132    data_array.io.ren.get(i) := io.ren(i)
133    valid_array.io.raddr(i) := io.raddr(i)
134    data_array.io.raddr(i) := io.raddr(i)
135
136    // gen result in rename stage
137    io.rdata(i).valid := valid_array.io.rdata(i)
138    io.rdata(i).ssid := data_array.io.rdata(i).ssid
139    io.rdata(i).strict := data_array.io.rdata(i).strict
140  }
141
142  // flush SSIT
143  // reset period: ResetTimeMax2Pow
144  val resetStepCounter = RegInit(0.U(log2Up(SSITSize + 1).W))
145  val s_idle :: s_flush :: Nil = Enum(2)
146  val state = RegInit(s_flush)
147
148  switch (state) {
149    is(s_idle) {
150      when(resetCounter(ResetTimeMax2Pow - 1, ResetTimeMin2Pow)(RegNext(io.csrCtrl.lvpred_timeout))) {
151        state := s_flush
152        resetCounter := 0.U
153      }
154    }
155    is(s_flush) {
156      when(resetStepCounter === (SSITSize - 1).U) {
157        state := s_idle // reset finished
158        resetStepCounter := 0.U
159      }.otherwise{
160        resetStepCounter := resetStepCounter + 1.U
161      }
162      valid_array.io.wen(SSIT_MISC_WRITE_PORT) := true.B
163      valid_array.io.waddr(SSIT_MISC_WRITE_PORT) := resetStepCounter
164      valid_array.io.wdata(SSIT_MISC_WRITE_PORT) := false.B
165      debug_valid(resetStepCounter) := false.B
166    }
167  }
168  XSPerfAccumulate("reset_timeout", state === s_flush && resetCounter === 0.U)
169
170  // update SSIT if load violation redirect is detected
171
172  // update stage 0: read ssit
173  val s1_mempred_update_req_valid = RegNext(io.update.valid)
174  val s1_mempred_update_req = RegEnable(io.update, io.update.valid)
175
176  // when io.update.valid, take over ssit read port
177  when (io.update.valid) {
178    valid_array.io.raddr(SSIT_UPDATE_LOAD_READ_PORT) := io.update.ldpc
179    valid_array.io.raddr(SSIT_UPDATE_STORE_READ_PORT) := io.update.stpc
180    data_array.io.raddr(SSIT_UPDATE_LOAD_READ_PORT) := io.update.ldpc
181    data_array.io.raddr(SSIT_UPDATE_STORE_READ_PORT) := io.update.stpc
182  }
183
184  // update stage 1: get ssit read result
185
186  // Read result
187  // load has already been assigned with a store set
188  val s1_loadAssigned = valid_array.io.rdata(SSIT_UPDATE_LOAD_READ_PORT)
189  val s1_loadOldSSID = data_array.io.rdata(SSIT_UPDATE_LOAD_READ_PORT).ssid
190  val s1_loadStrict = data_array.io.rdata(SSIT_UPDATE_LOAD_READ_PORT).strict
191  // store has already been assigned with a store set
192  val s1_storeAssigned = valid_array.io.rdata(SSIT_UPDATE_STORE_READ_PORT)
193  val s1_storeOldSSID = data_array.io.rdata(SSIT_UPDATE_STORE_READ_PORT).ssid
194  val s1_storeStrict = data_array.io.rdata(SSIT_UPDATE_STORE_READ_PORT).strict
195  // val s1_ssidIsSame = s1_loadOldSSID === s1_storeOldSSID
196
197  // update stage 2, update ssit data_array
198  val s2_mempred_update_req_valid = RegNext(s1_mempred_update_req_valid)
199  val s2_mempred_update_req = RegEnable(s1_mempred_update_req, s1_mempred_update_req_valid)
200  val s2_loadAssigned = RegEnable(s1_loadAssigned, s1_mempred_update_req_valid)
201  val s2_storeAssigned = RegEnable(s1_storeAssigned, s1_mempred_update_req_valid)
202  val s2_loadOldSSID = RegEnable(s1_loadOldSSID, s1_mempred_update_req_valid)
203  val s2_storeOldSSID = RegEnable(s1_storeOldSSID, s1_mempred_update_req_valid)
204  val s2_loadStrict = RegEnable(s1_loadStrict, s1_mempred_update_req_valid)
205
206  val s2_ssidIsSame = s2_loadOldSSID === s2_storeOldSSID
207  // for now we just use lowest bits of ldpc as store set id
208  val s2_ldSsidAllocate = XORFold(s2_mempred_update_req.ldpc, SSIDWidth)
209  val s2_stSsidAllocate = XORFold(s2_mempred_update_req.stpc, SSIDWidth)
210  val s2_allocSsid = Mux(s2_ldSsidAllocate < s2_stSsidAllocate, s2_ldSsidAllocate, s2_stSsidAllocate)
211  // both the load and the store have already been assigned store sets
212  // but load's store set ID is smaller
213  val s2_winnerSSID = Mux(s2_loadOldSSID < s2_storeOldSSID, s2_loadOldSSID, s2_storeOldSSID)
214
215  def update_ld_ssit_entry(pc: UInt, valid: Bool, ssid: UInt, strict: Bool) = {
216    valid_array.io.wen(SSIT_UPDATE_LOAD_WRITE_PORT) := true.B
217    valid_array.io.waddr(SSIT_UPDATE_LOAD_WRITE_PORT) := pc
218    valid_array.io.wdata(SSIT_UPDATE_LOAD_WRITE_PORT) := valid
219    data_array.io.wen(SSIT_UPDATE_LOAD_WRITE_PORT) := true.B
220    data_array.io.waddr(SSIT_UPDATE_LOAD_WRITE_PORT) := pc
221    data_array.io.wdata(SSIT_UPDATE_LOAD_WRITE_PORT).ssid := ssid
222    data_array.io.wdata(SSIT_UPDATE_LOAD_WRITE_PORT).strict := strict
223    debug_valid(pc) := valid
224    debug_ssid(pc) := ssid
225    debug_strict(pc) := strict
226  }
227
228  def update_st_ssit_entry(pc: UInt, valid: Bool, ssid: UInt, strict: Bool) = {
229    valid_array.io.wen(SSIT_UPDATE_STORE_WRITE_PORT) := true.B
230    valid_array.io.waddr(SSIT_UPDATE_STORE_WRITE_PORT) := pc
231    valid_array.io.wdata(SSIT_UPDATE_STORE_WRITE_PORT):= valid
232    data_array.io.wen(SSIT_UPDATE_STORE_WRITE_PORT) := true.B
233    data_array.io.waddr(SSIT_UPDATE_STORE_WRITE_PORT) := pc
234    data_array.io.wdata(SSIT_UPDATE_STORE_WRITE_PORT).ssid := ssid
235    data_array.io.wdata(SSIT_UPDATE_STORE_WRITE_PORT).strict := strict
236    debug_valid(pc) := valid
237    debug_ssid(pc) := ssid
238    debug_strict(pc) := strict
239  }
240
241  when(s2_mempred_update_req_valid){
242    switch (Cat(s2_loadAssigned, s2_storeAssigned)) {
243      // 1. "If neither the load nor the store has been assigned a store set,
244      // two are allocated and assigned to each instruction."
245      is ("b00".U(2.W)) {
246        update_ld_ssit_entry(
247          pc = s2_mempred_update_req.ldpc,
248          valid = true.B,
249          ssid = s2_allocSsid,
250          strict = false.B
251        )
252        update_st_ssit_entry(
253          pc = s2_mempred_update_req.stpc,
254          valid = true.B,
255          ssid = s2_allocSsid,
256          strict = false.B
257        )
258      }
259      // 2. "If the load has been assigned a store set, but the store has not,
260      // one is allocated and assigned to the store instructions."
261      is ("b10".U(2.W)) {
262        update_st_ssit_entry(
263          pc = s2_mempred_update_req.stpc,
264          valid = true.B,
265          ssid = s2_ldSsidAllocate,
266          strict = false.B
267        )
268      }
269      // 3. "If the store has been assigned a store set, but the load has not,
270      // one is allocated and assigned to the load instructions."
271      is ("b01".U(2.W)) {
272        update_ld_ssit_entry(
273          pc = s2_mempred_update_req.ldpc,
274          valid = true.B,
275          ssid = s2_stSsidAllocate,
276          strict = false.B
277        )
278      }
279      // 4. "If both the load and the store have already been assigned store sets,
280      // one of the two store sets is declared the "winner".
281      // The instruction belonging to the loser’s store set is assigned the winner’s store set."
282      is ("b11".U(2.W)) {
283        update_ld_ssit_entry(
284          pc = s2_mempred_update_req.ldpc,
285          valid = true.B,
286          ssid = s2_winnerSSID,
287          strict = false.B
288        )
289        update_st_ssit_entry(
290          pc = s2_mempred_update_req.stpc,
291          valid = true.B,
292          ssid = s2_winnerSSID,
293          strict = false.B
294        )
295        when(s2_ssidIsSame){
296          data_array.io.wdata(SSIT_UPDATE_LOAD_READ_PORT).strict := true.B
297          debug_strict(s2_mempred_update_req.ldpc) := true.B
298        }
299      }
300    }
301  }
302
303  // make SyncDataModuleTemplate happy
304  when(valid_array.io.waddr(SSIT_UPDATE_LOAD_WRITE_PORT) === valid_array.io.waddr(SSIT_UPDATE_STORE_WRITE_PORT)){
305    valid_array.io.wen(SSIT_UPDATE_STORE_WRITE_PORT) := false.B
306  }
307
308  when(data_array.io.waddr(SSIT_UPDATE_LOAD_WRITE_PORT) === data_array.io.waddr(SSIT_UPDATE_STORE_WRITE_PORT)){
309    data_array.io.wen(SSIT_UPDATE_STORE_WRITE_PORT) := false.B
310  }
311
312  XSPerfAccumulate("ssit_update_lxsx", s2_mempred_update_req_valid && !s2_loadAssigned && !s2_storeAssigned)
313  XSPerfAccumulate("ssit_update_lysx", s2_mempred_update_req_valid && s2_loadAssigned && !s2_storeAssigned)
314  XSPerfAccumulate("ssit_update_lxsy", s2_mempred_update_req_valid && !s2_loadAssigned && s2_storeAssigned)
315  XSPerfAccumulate("ssit_update_lysy", s2_mempred_update_req_valid && s2_loadAssigned && s2_storeAssigned)
316  XSPerfAccumulate("ssit_update_should_strict", s2_mempred_update_req_valid && s2_ssidIsSame && s2_loadAssigned && s2_storeAssigned)
317  XSPerfAccumulate("ssit_update_strict_failed",
318    s2_mempred_update_req_valid && s2_ssidIsSame && s2_loadStrict && s2_loadAssigned && s2_storeAssigned
319  ) // should be zero
320
321  // debug
322  XSDebug(s2_mempred_update_req.valid, "%d: SSIT update: load pc %x store pc %x\n", GTimer(), s2_mempred_update_req.ldpc, s2_mempred_update_req.stpc)
323  XSDebug(s2_mempred_update_req.valid, "%d: SSIT update: load valid %b ssid %x  store valid %b ssid %x\n", GTimer(), s2_loadAssigned, s2_loadOldSSID, s2_storeAssigned, s2_storeOldSSID)
324}
325
326
327// Last Fetched Store Table Entry
328class LFSTEntry(implicit p: Parameters) extends XSBundle  {
329  val valid = Bool()
330  val robIdx = new RobPtr
331}
332
333class LFSTReq(implicit p: Parameters) extends XSBundle {
334  val isstore = Bool()
335  val ssid = UInt(SSIDWidth.W) // use ssid to lookup LFST
336  val robIdx = new RobPtr
337}
338
339class LFSTResp(implicit p: Parameters) extends XSBundle {
340  val shouldWait = Bool()
341  val robIdx = new RobPtr
342}
343
344class DispatchLFSTIO(implicit p: Parameters) extends XSBundle {
345  val req = Vec(RenameWidth, Valid(new LFSTReq))
346  val resp = Vec(RenameWidth, Flipped(Valid(new LFSTResp)))
347}
348
349// Last Fetched Store Table
350class LFST(implicit p: Parameters) extends XSModule {
351  val io = IO(new Bundle {
352    // when redirect, mark canceled store as invalid
353    val redirect = Input(Valid(new Redirect))
354    val dispatch = Flipped(new DispatchLFSTIO)
355    // when store issued, mark store as invalid
356    val storeIssue = Vec(backendParams.StaExuCnt, Flipped(Valid(new DynInst)))
357    val csrCtrl = Input(new CustomCSRCtrlIO)
358  })
359
360  val validVec = RegInit(VecInit(Seq.fill(LFSTSize)(VecInit(Seq.fill(LFSTWidth)(false.B)))))
361  val robIdxVec = Reg(Vec(LFSTSize, Vec(LFSTWidth, new RobPtr)))
362  val allocPtr = RegInit(VecInit(Seq.fill(LFSTSize)(0.U(log2Up(LFSTWidth).W))))
363  val valid = Wire(Vec(LFSTSize, Bool()))
364  (0 until LFSTSize).map(i => {
365    valid(i) := validVec(i).asUInt.orR
366  })
367
368  // read LFST in rename stage
369  for (i <- 0 until RenameWidth) {
370    io.dispatch.resp(i).valid := io.dispatch.req(i).valid
371
372    // If store-load pair is in the same dispatch bundle, loadWaitBit should also be set for load
373    val hitInDispatchBundleVec = if(i > 0){
374      WireInit(VecInit((0 until i).map(j =>
375        io.dispatch.req(j).valid &&
376        io.dispatch.req(j).bits.isstore &&
377        io.dispatch.req(j).bits.ssid === io.dispatch.req(i).bits.ssid
378      )))
379    } else {
380      WireInit(VecInit(Seq(false.B))) // DontCare
381    }
382    val hitInDispatchBundle = hitInDispatchBundleVec.asUInt.orR
383    // Check if store set is valid in LFST
384    io.dispatch.resp(i).bits.shouldWait := (
385        (valid(io.dispatch.req(i).bits.ssid) || hitInDispatchBundle) &&
386        io.dispatch.req(i).valid &&
387        (!io.dispatch.req(i).bits.isstore || io.csrCtrl.storeset_wait_store)
388      ) && !io.csrCtrl.lvpred_disable || io.csrCtrl.no_spec_load
389    io.dispatch.resp(i).bits.robIdx := robIdxVec(io.dispatch.req(i).bits.ssid)(allocPtr(io.dispatch.req(i).bits.ssid)-1.U)
390    if(i > 0){
391      (0 until i).map(j =>
392        when(hitInDispatchBundleVec(j)){
393          io.dispatch.resp(i).bits.robIdx := io.dispatch.req(j).bits.robIdx
394        }
395      )
396    }
397  }
398
399  // when store is issued, mark it as invalid
400  (0 until backendParams.StaExuCnt).map(i => {
401    // TODO: opt timing
402    (0 until LFSTWidth).map(j => {
403      when(io.storeIssue(i).valid && io.storeIssue(i).bits.storeSetHit && io.storeIssue(i).bits.robIdx.value === robIdxVec(io.storeIssue(i).bits.ssid)(j).value){
404        validVec(io.storeIssue(i).bits.ssid)(j) := false.B
405      }
406    })
407  })
408
409  // when store is dispatched, mark it as valid
410  (0 until RenameWidth).map(i => {
411    when(io.dispatch.req(i).valid && io.dispatch.req(i).bits.isstore){
412      val waddr = io.dispatch.req(i).bits.ssid
413      val wptr = allocPtr(waddr)
414      allocPtr(waddr) := allocPtr(waddr) + 1.U
415      validVec(waddr)(wptr) := true.B
416      robIdxVec(waddr)(wptr) := io.dispatch.req(i).bits.robIdx
417    }
418  })
419
420  // when redirect, cancel store influenced
421  (0 until LFSTSize).map(i => {
422    (0 until LFSTWidth).map(j => {
423      when(validVec(i)(j) && robIdxVec(i)(j).needFlush(io.redirect)){
424        validVec(i)(j) := false.B
425      }
426    })
427  })
428
429  // recover robIdx after squash
430  // behavior model, to be refactored later
431  when(RegNext(io.redirect.fire)) {
432    (0 until LFSTSize).map(i => {
433      (0 until LFSTWidth).map(j => {
434        val check_position = WireInit(allocPtr(i) + (j+1).U)
435        when(!validVec(i)(check_position)){
436          allocPtr(i) := check_position
437        }
438      })
439    })
440  }
441}
442