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