xref: /XiangShan/src/main/scala/xiangshan/mem/mdp/StoreSet.scala (revision 2f0b133ce2e980d3351906d001bd6027612c9a80)
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 resetStepCounterFull = resetStepCounter(log2Up(SSITSize))
130  val s_idle :: s_flush :: Nil = Enum(2)
131  val state = RegInit(s_flush)
132
133  switch (state) {
134    is(s_idle) {
135      when(resetCounter(ResetTimeMax2Pow-1, ResetTimeMin2Pow)(RegNext(io.csrCtrl.lvpred_timeout))) {
136        state := s_flush
137        resetCounter := 0.U
138      }
139    }
140    is(s_flush) {
141      when(resetStepCounterFull) {
142        state := s_idle // reset finished
143        resetStepCounter := 0.U
144      }.otherwise{
145        valid_array.io.wen(SSIT_MISC_WRITE_PORT) := true.B
146        valid_array.io.waddr(SSIT_MISC_WRITE_PORT) := resetStepCounter
147        valid_array.io.wdata(SSIT_MISC_WRITE_PORT) := false.B
148        debug_valid(resetStepCounter) := false.B
149        resetStepCounter := resetStepCounter + 1.U
150      }
151    }
152  }
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_ssidAllocate = s1_mempred_update_req.ldpc(SSIDWidth-1, 0)
193  // both the load and the store have already been assigned store sets
194  // but load's store set ID is smaller
195  val s2_winnerSSID = Mux(s2_loadOldSSID < s2_storeOldSSID, s2_loadOldSSID, s2_storeOldSSID)
196
197  def update_ld_ssit_entry(pc: UInt, valid: Bool, ssid: UInt, strict: Bool) = {
198    valid_array.io.wen(SSIT_UPDATE_LOAD_WRITE_PORT) := true.B
199    valid_array.io.waddr(SSIT_UPDATE_LOAD_WRITE_PORT) := pc
200    valid_array.io.wdata(SSIT_UPDATE_LOAD_WRITE_PORT) := valid
201    data_array.io.wen(SSIT_UPDATE_LOAD_WRITE_PORT) := true.B
202    data_array.io.waddr(SSIT_UPDATE_LOAD_WRITE_PORT) := pc
203    data_array.io.wdata(SSIT_UPDATE_LOAD_WRITE_PORT).ssid := ssid
204    data_array.io.wdata(SSIT_UPDATE_LOAD_WRITE_PORT).strict := strict
205    debug_valid(pc) := valid
206    debug_ssid(pc) := ssid
207    debug_strict(pc) := strict
208  }
209
210  def update_st_ssit_entry(pc: UInt, valid: Bool, ssid: UInt, strict: Bool) = {
211    valid_array.io.wen(SSIT_UPDATE_STORE_WRITE_PORT) := true.B
212    valid_array.io.waddr(SSIT_UPDATE_STORE_WRITE_PORT) := pc
213    valid_array.io.wdata(SSIT_UPDATE_STORE_WRITE_PORT):= valid
214    data_array.io.wen(SSIT_UPDATE_STORE_WRITE_PORT) := true.B
215    data_array.io.waddr(SSIT_UPDATE_STORE_WRITE_PORT) := pc
216    data_array.io.wdata(SSIT_UPDATE_STORE_WRITE_PORT).ssid := ssid
217    data_array.io.wdata(SSIT_UPDATE_STORE_WRITE_PORT).strict := strict
218    debug_valid(pc) := valid
219    debug_ssid(pc) := ssid
220    debug_strict(pc) := strict
221  }
222
223  when(s2_mempred_update_req_valid){
224    switch (Cat(s2_loadAssigned, s2_storeAssigned)) {
225      // 1. "If neither the load nor the store has been assigned a store set,
226      // one is allocated and assigned to both instructions."
227      is ("b00".U(2.W)) {
228        update_ld_ssit_entry(
229          pc = s2_mempred_update_req.ldpc,
230          valid = true.B,
231          ssid = s2_ssidAllocate,
232          strict = false.B
233        )
234        update_st_ssit_entry(
235          pc = s2_mempred_update_req.stpc,
236          valid = true.B,
237          ssid = s2_ssidAllocate,
238          strict = false.B
239        )
240      }
241      // 2. "If the load has been assigned a store set, but the store has not,
242      // the store is assigned the load’s store set."
243      is ("b10".U(2.W)) {
244        update_st_ssit_entry(
245          pc = s2_mempred_update_req.stpc,
246          valid = true.B,
247          ssid = s2_loadOldSSID,
248          strict = false.B
249        )
250      }
251      // 3. "If the store has been assigned a store set, but the load has not,
252      // the load is assigned the store’s store set."
253      is ("b01".U(2.W)) {
254        update_ld_ssit_entry(
255          pc = s2_mempred_update_req.ldpc,
256          valid = true.B,
257          ssid = s2_storeOldSSID,
258          strict = false.B
259        )
260      }
261      // 4. "If both the load and the store have already been assigned store sets,
262      // one of the two store sets is declared the "winner".
263      // The instruction belonging to the loser’s store set is assigned the winner’s store set."
264      is ("b11".U(2.W)) {
265        update_ld_ssit_entry(
266          pc = s2_mempred_update_req.ldpc,
267          valid = true.B,
268          ssid = s2_winnerSSID,
269          strict = false.B
270        )
271        update_st_ssit_entry(
272          pc = s2_mempred_update_req.stpc,
273          valid = true.B,
274          ssid = s2_winnerSSID,
275          strict = false.B
276        )
277        when(s2_ssidIsSame){
278          data_array.io.wdata(SSIT_UPDATE_LOAD_READ_PORT).strict := true.B
279          debug_strict(s2_mempred_update_req.ldpc) := true.B
280        }
281      }
282    }
283  }
284
285  // make SyncDataModuleTemplate happy
286  when(valid_array.io.waddr(SSIT_UPDATE_LOAD_WRITE_PORT) === valid_array.io.waddr(SSIT_UPDATE_STORE_WRITE_PORT)){
287    valid_array.io.wen(SSIT_UPDATE_STORE_WRITE_PORT) := false.B
288  }
289
290  when(data_array.io.waddr(SSIT_UPDATE_LOAD_WRITE_PORT) === data_array.io.waddr(SSIT_UPDATE_STORE_WRITE_PORT)){
291    data_array.io.wen(SSIT_UPDATE_STORE_WRITE_PORT) := false.B
292  }
293
294  XSPerfAccumulate("ssit_update_lxsx", s2_mempred_update_req_valid && !s2_loadAssigned && !s2_storeAssigned)
295  XSPerfAccumulate("ssit_update_lysx", s2_mempred_update_req_valid && s2_loadAssigned && !s2_storeAssigned)
296  XSPerfAccumulate("ssit_update_lxsy", s2_mempred_update_req_valid && !s2_loadAssigned && s2_storeAssigned)
297  XSPerfAccumulate("ssit_update_lysy", s2_mempred_update_req_valid && s2_loadAssigned && s2_storeAssigned)
298  XSPerfAccumulate("ssit_update_should_strict", s2_mempred_update_req_valid && s2_ssidIsSame && s2_loadAssigned && s2_storeAssigned)
299  XSPerfAccumulate("ssit_update_strict_failed",
300    s2_mempred_update_req_valid && s2_ssidIsSame && s2_loadStrict && s2_loadAssigned && s2_storeAssigned
301  ) // should be zero
302
303
304  // debug
305  for (i <- 0 until StorePipelineWidth) {
306    when (s2_mempred_update_req.valid) {
307      XSDebug("%d: SSIT update: load pc %x store pc %x\n", GTimer(), s2_mempred_update_req.ldpc, s2_mempred_update_req.stpc)
308      XSDebug("%d: SSIT update: load valid %b ssid %x  store valid %b ssid %x\n", GTimer(), s2_loadAssigned, s2_loadOldSSID, s2_storeAssigned, s2_storeOldSSID)
309    }
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(exuParameters.StuCnt, Flipped(Valid(new ExuInput)))
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(i).bits.robIdx
381        }
382      )
383    }
384  }
385
386  // when store is issued, mark it as invalid
387  (0 until exuParameters.StuCnt).map(i => {
388    // TODO: opt timing
389    (0 until LFSTWidth).map(j => {
390      when(io.storeIssue(i).valid && io.storeIssue(i).bits.uop.robIdx.value === robIdxVec(io.storeIssue(i).bits.uop.cf.ssid)(j).value){
391        validVec(io.storeIssue(i).bits.uop.cf.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(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