xref: /XiangShan/src/main/scala/xiangshan/mem/prefetch/L1StridePrefetcher.scala (revision 70eea123e785738636fd949014df5cc7647d2e65)
10d32f713Shappy-lxpackage xiangshan.mem.prefetch
20d32f713Shappy-lx
38891a219SYinan Xuimport org.chipsalliance.cde.config.Parameters
40d32f713Shappy-lximport chisel3._
50d32f713Shappy-lximport chisel3.util._
60d32f713Shappy-lximport xiangshan._
70d32f713Shappy-lximport utils._
80d32f713Shappy-lximport utility._
90d32f713Shappy-lximport xiangshan.cache.HasDCacheParameters
100d32f713Shappy-lximport xiangshan.cache.mmu._
110d32f713Shappy-lximport xiangshan.mem.{L1PrefetchReq, LdPrefetchTrainBundle}
120d32f713Shappy-lximport xiangshan.mem.trace._
130d32f713Shappy-lximport scala.collection.SeqLike
140d32f713Shappy-lx
150d32f713Shappy-lxtrait HasStridePrefetchHelper extends HasL1PrefetchHelper {
160d32f713Shappy-lx  val STRIDE_FILTER_SIZE = 6
170d32f713Shappy-lx  val STRIDE_ENTRY_NUM = 10
180d32f713Shappy-lx  val STRIDE_BITS = 10 + BLOCK_OFFSET
190d32f713Shappy-lx  val STRIDE_VADDR_BITS = 10 + BLOCK_OFFSET
200d32f713Shappy-lx  val STRIDE_CONF_BITS = 2
210d32f713Shappy-lx
220d32f713Shappy-lx  // detail control
23c686adcdSYinan Xu  val ALWAYS_UPDATE_PRE_VADDR = true
240d32f713Shappy-lx  val AGGRESIVE_POLICY = false // if true, prefetch degree is greater than 1, 1 otherwise
250d32f713Shappy-lx  val STRIDE_LOOK_AHEAD_BLOCKS = 2 // aggressive degree
260d32f713Shappy-lx  val LOOK_UP_STREAM = false // if true, avoid collision with stream
270d32f713Shappy-lx
280d32f713Shappy-lx  val STRIDE_WIDTH_BLOCKS = if(AGGRESIVE_POLICY) STRIDE_LOOK_AHEAD_BLOCKS else 1
290d32f713Shappy-lx
300d32f713Shappy-lx  def MAX_CONF = (1 << STRIDE_CONF_BITS) - 1
310d32f713Shappy-lx}
320d32f713Shappy-lx
330d32f713Shappy-lxclass StrideMetaBundle(implicit p: Parameters) extends XSBundle with HasStridePrefetchHelper {
340d32f713Shappy-lx  val pre_vaddr = UInt(STRIDE_VADDR_BITS.W)
350d32f713Shappy-lx  val stride = UInt(STRIDE_BITS.W)
360d32f713Shappy-lx  val confidence = UInt(STRIDE_CONF_BITS.W)
370d32f713Shappy-lx  val hash_pc = UInt(HASH_TAG_WIDTH.W)
380d32f713Shappy-lx
390d32f713Shappy-lx  def reset(index: Int) = {
400d32f713Shappy-lx    pre_vaddr := 0.U
410d32f713Shappy-lx    stride := 0.U
420d32f713Shappy-lx    confidence := 0.U
430d32f713Shappy-lx    hash_pc := index.U
440d32f713Shappy-lx  }
450d32f713Shappy-lx
46*70eea123SYanqin Li  def tag_match(valid1: Bool, valid2: Bool, new_hash_pc: UInt): Bool = {
47*70eea123SYanqin Li    valid1 && valid2 && hash_pc === new_hash_pc
48*70eea123SYanqin Li  }
49*70eea123SYanqin Li
500d32f713Shappy-lx  def alloc(vaddr: UInt, alloc_hash_pc: UInt) = {
510d32f713Shappy-lx    pre_vaddr := vaddr(STRIDE_VADDR_BITS - 1, 0)
520d32f713Shappy-lx    stride := 0.U
530d32f713Shappy-lx    confidence := 0.U
540d32f713Shappy-lx    hash_pc := alloc_hash_pc
550d32f713Shappy-lx  }
560d32f713Shappy-lx
570d32f713Shappy-lx  def update(vaddr: UInt, always_update_pre_vaddr: Bool) = {
580d32f713Shappy-lx    val new_vaddr = vaddr(STRIDE_VADDR_BITS - 1, 0)
590d32f713Shappy-lx    val new_stride = new_vaddr - pre_vaddr
600d32f713Shappy-lx    val new_stride_blk = block_addr(new_stride)
610d32f713Shappy-lx    // NOTE: for now, disable negtive stride
620d32f713Shappy-lx    val stride_valid = new_stride_blk =/= 0.U && new_stride_blk =/= 1.U && new_stride(STRIDE_VADDR_BITS - 1) === 0.U
630d32f713Shappy-lx    val stride_match = new_stride === stride
640d32f713Shappy-lx    val low_confidence = confidence <= 1.U
650d32f713Shappy-lx    val can_send_pf = stride_valid && stride_match && confidence === MAX_CONF.U
660d32f713Shappy-lx
670d32f713Shappy-lx    when(stride_valid) {
680d32f713Shappy-lx      when(stride_match) {
690d32f713Shappy-lx        confidence := Mux(confidence === MAX_CONF.U, confidence, confidence + 1.U)
700d32f713Shappy-lx      }.otherwise {
710d32f713Shappy-lx        confidence := Mux(confidence === 0.U, confidence, confidence - 1.U)
720d32f713Shappy-lx        when(low_confidence) {
730d32f713Shappy-lx          stride := new_stride
740d32f713Shappy-lx        }
750d32f713Shappy-lx      }
760d32f713Shappy-lx      pre_vaddr := new_vaddr
770d32f713Shappy-lx    }
780d32f713Shappy-lx    when(always_update_pre_vaddr) {
790d32f713Shappy-lx      pre_vaddr := new_vaddr
800d32f713Shappy-lx    }
810d32f713Shappy-lx
820d32f713Shappy-lx    (can_send_pf, new_stride)
830d32f713Shappy-lx  }
840d32f713Shappy-lx
850d32f713Shappy-lx}
860d32f713Shappy-lx
870d32f713Shappy-lxclass StrideMetaArray(implicit p: Parameters) extends XSModule with HasStridePrefetchHelper {
880d32f713Shappy-lx  val io = IO(new XSBundle {
890d32f713Shappy-lx    val enable = Input(Bool())
900d32f713Shappy-lx    // TODO: flush all entry when process changing happens, or disable stream prefetch for a while
910d32f713Shappy-lx    val flush = Input(Bool())
920d32f713Shappy-lx    val dynamic_depth = Input(UInt(32.W)) // TODO: enable dynamic stride depth
930d32f713Shappy-lx    val train_req = Flipped(DecoupledIO(new PrefetchReqBundle))
9420e09ab1Shappy-lx    val l1_prefetch_req = ValidIO(new StreamPrefetchReqBundle)
9520e09ab1Shappy-lx    val l2_l3_prefetch_req = ValidIO(new StreamPrefetchReqBundle)
960d32f713Shappy-lx    // query Stream component to see if a stream pattern has already been detected
970d32f713Shappy-lx    val stream_lookup_req  = ValidIO(new PrefetchReqBundle)
980d32f713Shappy-lx    val stream_lookup_resp = Input(Bool())
990d32f713Shappy-lx  })
1000d32f713Shappy-lx
1010d32f713Shappy-lx  val array = Reg(Vec(STRIDE_ENTRY_NUM, new StrideMetaBundle))
102*70eea123SYanqin Li  val valids = RegInit(VecInit(Seq.fill(STRIDE_ENTRY_NUM)(false.B)))
103*70eea123SYanqin Li
104*70eea123SYanqin Li  def reset_array(i: Int): Unit = {
105*70eea123SYanqin Li    valids(i) := false.B
106*70eea123SYanqin Li    //only need to rest control signals for firendly area
107*70eea123SYanqin Li    // array(i).reset(i)
108*70eea123SYanqin Li  }
109*70eea123SYanqin Li
1100d32f713Shappy-lx  val replacement = ReplacementPolicy.fromString("plru", STRIDE_ENTRY_NUM)
1110d32f713Shappy-lx
1120d32f713Shappy-lx  // s0: hash pc -> cam all entries
1130d32f713Shappy-lx  val s0_can_accept = Wire(Bool())
1140d32f713Shappy-lx  val s0_valid = io.train_req.fire
1150d32f713Shappy-lx  val s0_vaddr = io.train_req.bits.vaddr
1160d32f713Shappy-lx  val s0_pc = io.train_req.bits.pc
1170d32f713Shappy-lx  val s0_pc_hash = pc_hash_tag(s0_pc)
118*70eea123SYanqin Li  val s0_pc_match_vec = VecInit(array zip valids map { case (e, v) => e.tag_match(v, s0_valid, s0_pc_hash) }).asUInt
1190d32f713Shappy-lx  val s0_hit = s0_pc_match_vec.orR
1200d32f713Shappy-lx  val s0_index = Mux(s0_hit, OHToUInt(s0_pc_match_vec), replacement.way)
1210d32f713Shappy-lx  io.train_req.ready := s0_can_accept
1220d32f713Shappy-lx  io.stream_lookup_req.valid := s0_valid
1230d32f713Shappy-lx  io.stream_lookup_req.bits  := io.train_req.bits
1240d32f713Shappy-lx
1250d32f713Shappy-lx  when(s0_valid) {
1260d32f713Shappy-lx    replacement.access(s0_index)
1270d32f713Shappy-lx  }
1280d32f713Shappy-lx
1290d32f713Shappy-lx  assert(PopCount(s0_pc_match_vec) <= 1.U)
1300d32f713Shappy-lx  XSPerfAccumulate("s0_valid", s0_valid)
1310d32f713Shappy-lx  XSPerfAccumulate("s0_hit", s0_valid && s0_hit)
1320d32f713Shappy-lx  XSPerfAccumulate("s0_miss", s0_valid && !s0_hit)
1330d32f713Shappy-lx
1340d32f713Shappy-lx  // s1: alloc or update
1354ccb2e8bSYanqin Li  val s1_valid = GatedValidRegNext(s0_valid)
1360d32f713Shappy-lx  val s1_index = RegEnable(s0_index, s0_valid)
1370d32f713Shappy-lx  val s1_pc_hash = RegEnable(s0_pc_hash, s0_valid)
1380d32f713Shappy-lx  val s1_vaddr = RegEnable(s0_vaddr, s0_valid)
1390d32f713Shappy-lx  val s1_hit = RegEnable(s0_hit, s0_valid)
1400d32f713Shappy-lx  val s1_alloc = s1_valid && !s1_hit
1410d32f713Shappy-lx  val s1_update = s1_valid && s1_hit
1420d32f713Shappy-lx  val s1_stride = array(s1_index).stride
1430d32f713Shappy-lx  val s1_new_stride = WireInit(0.U(STRIDE_BITS.W))
1440d32f713Shappy-lx  val s1_can_send_pf = WireInit(false.B)
1450d32f713Shappy-lx  s0_can_accept := !(s1_valid && s1_pc_hash === s0_pc_hash)
1460d32f713Shappy-lx
147c686adcdSYinan Xu  val always_update = Constantin.createRecord(s"always_update${p(XSCoreParamsKey).HartId}", initValue = ALWAYS_UPDATE_PRE_VADDR)
1480d32f713Shappy-lx
1490d32f713Shappy-lx  when(s1_alloc) {
150*70eea123SYanqin Li    valids(s1_index) := true.B
1510d32f713Shappy-lx    array(s1_index).alloc(
1520d32f713Shappy-lx      vaddr = s1_vaddr,
1530d32f713Shappy-lx      alloc_hash_pc = s1_pc_hash
1540d32f713Shappy-lx    )
1550d32f713Shappy-lx  }.elsewhen(s1_update) {
1560d32f713Shappy-lx    val res = array(s1_index).update(s1_vaddr, always_update)
1570d32f713Shappy-lx    s1_can_send_pf := res._1
1580d32f713Shappy-lx    s1_new_stride := res._2
1590d32f713Shappy-lx  }
1600d32f713Shappy-lx
161c686adcdSYinan Xu  val l1_stride_ratio_const = Constantin.createRecord(s"l1_stride_ratio${p(XSCoreParamsKey).HartId}", initValue = 2)
1620d32f713Shappy-lx  val l1_stride_ratio = l1_stride_ratio_const(3, 0)
163c686adcdSYinan Xu  val l2_stride_ratio_const = Constantin.createRecord(s"l2_stride_ratio${p(XSCoreParamsKey).HartId}", initValue = 5)
1640d32f713Shappy-lx  val l2_stride_ratio = l2_stride_ratio_const(3, 0)
1650d32f713Shappy-lx  // s2: calculate L1 & L2 pf addr
1664ccb2e8bSYanqin Li  val s2_valid = GatedValidRegNext(s1_valid && s1_can_send_pf)
1670d32f713Shappy-lx  val s2_vaddr = RegEnable(s1_vaddr, s1_valid && s1_can_send_pf)
1680d32f713Shappy-lx  val s2_stride = RegEnable(s1_stride, s1_valid && s1_can_send_pf)
1690d32f713Shappy-lx  val s2_l1_depth = s2_stride << l1_stride_ratio
1700d32f713Shappy-lx  val s2_l1_pf_vaddr = (s2_vaddr + s2_l1_depth)(VAddrBits - 1, 0)
1710d32f713Shappy-lx  val s2_l2_depth = s2_stride << l2_stride_ratio
1720d32f713Shappy-lx  val s2_l2_pf_vaddr = (s2_vaddr + s2_l2_depth)(VAddrBits - 1, 0)
1730d32f713Shappy-lx  val s2_l1_pf_req_bits = (new StreamPrefetchReqBundle).getStreamPrefetchReqBundle(
17458a9a40dSTang Haojin    valid = s2_valid,
1750d32f713Shappy-lx    vaddr = s2_l1_pf_vaddr,
1760d32f713Shappy-lx    width = STRIDE_WIDTH_BLOCKS,
1770d32f713Shappy-lx    decr_mode = false.B,
1780d32f713Shappy-lx    sink = SINK_L1,
17920e09ab1Shappy-lx    source = L1_HW_PREFETCH_STRIDE,
18020e09ab1Shappy-lx    // TODO: add stride debug db, not useful for now
18120e09ab1Shappy-lx    t_pc = 0xdeadbeefL.U,
18220e09ab1Shappy-lx    t_va = 0xdeadbeefL.U
18320e09ab1Shappy-lx    )
1840d32f713Shappy-lx  val s2_l2_pf_req_bits = (new StreamPrefetchReqBundle).getStreamPrefetchReqBundle(
18558a9a40dSTang Haojin    valid = s2_valid,
1860d32f713Shappy-lx    vaddr = s2_l2_pf_vaddr,
1870d32f713Shappy-lx    width = STRIDE_WIDTH_BLOCKS,
1880d32f713Shappy-lx    decr_mode = false.B,
1890d32f713Shappy-lx    sink = SINK_L2,
19020e09ab1Shappy-lx    source = L1_HW_PREFETCH_STRIDE,
19120e09ab1Shappy-lx    // TODO: add stride debug db, not useful for now
19220e09ab1Shappy-lx    t_pc = 0xdeadbeefL.U,
19320e09ab1Shappy-lx    t_va = 0xdeadbeefL.U
19420e09ab1Shappy-lx    )
1950d32f713Shappy-lx
1960d32f713Shappy-lx  // s3: send l1 pf out
1974ccb2e8bSYanqin Li  val s3_valid = if (LOOK_UP_STREAM) GatedValidRegNext(s2_valid) && !io.stream_lookup_resp else GatedValidRegNext(s2_valid)
1980d32f713Shappy-lx  val s3_l1_pf_req_bits = RegEnable(s2_l1_pf_req_bits, s2_valid)
1990d32f713Shappy-lx  val s3_l2_pf_req_bits = RegEnable(s2_l2_pf_req_bits, s2_valid)
2000d32f713Shappy-lx
2010d32f713Shappy-lx  // s4: send l2 pf out
2024ccb2e8bSYanqin Li  val s4_valid = GatedValidRegNext(s3_valid)
2030d32f713Shappy-lx  val s4_l2_pf_req_bits = RegEnable(s3_l2_pf_req_bits, s3_valid)
2040d32f713Shappy-lx
20520e09ab1Shappy-lx  io.l1_prefetch_req.valid := s3_valid
20620e09ab1Shappy-lx  io.l1_prefetch_req.bits := s3_l1_pf_req_bits
20720e09ab1Shappy-lx  io.l2_l3_prefetch_req.valid := s4_valid
20820e09ab1Shappy-lx  io.l2_l3_prefetch_req.bits := s4_l2_pf_req_bits
2090d32f713Shappy-lx
21020e09ab1Shappy-lx  XSPerfAccumulate("pf_valid", PopCount(Seq(io.l1_prefetch_req.valid, io.l2_l3_prefetch_req.valid)))
21120e09ab1Shappy-lx  XSPerfAccumulate("l1_pf_valid", s3_valid)
2120d32f713Shappy-lx  XSPerfAccumulate("l2_pf_valid", s4_valid)
2130d32f713Shappy-lx  XSPerfAccumulate("detect_stream", io.stream_lookup_resp)
2140d32f713Shappy-lx  XSPerfHistogram("high_conf_num", PopCount(VecInit(array.map(_.confidence === MAX_CONF.U))).asUInt, true.B, 0, STRIDE_ENTRY_NUM, 1)
2150d32f713Shappy-lx  for(i <- 0 until STRIDE_ENTRY_NUM) {
2160d32f713Shappy-lx    XSPerfAccumulate(s"entry_${i}_update", i.U === s1_index && s1_update)
2170d32f713Shappy-lx    for(j <- 0 until 4) {
2180d32f713Shappy-lx      XSPerfAccumulate(s"entry_${i}_disturb_${j}", i.U === s1_index && s1_update &&
2190d32f713Shappy-lx                                                   j.U === s1_new_stride &&
2200d32f713Shappy-lx                                                   array(s1_index).confidence === MAX_CONF.U &&
2210d32f713Shappy-lx                                                   array(s1_index).stride =/= s1_new_stride
2220d32f713Shappy-lx      )
2230d32f713Shappy-lx    }
2240d32f713Shappy-lx  }
2250d32f713Shappy-lx
2260d32f713Shappy-lx  for(i <- 0 until STRIDE_ENTRY_NUM) {
227*70eea123SYanqin Li    when(GatedValidRegNext(io.flush)) {
228*70eea123SYanqin Li      reset_array(i)
2290d32f713Shappy-lx    }
2300d32f713Shappy-lx  }
2310d32f713Shappy-lx}