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