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