/***************************************************************************************
  * Copyright (c) 2020-2021 Institute of Computing Technology, Chinese Academy of Sciences
  * Copyright (c) 2020-2021 Peng Cheng Laboratory
  *
  * XiangShan is licensed under Mulan PSL v2.
  * You can use this software according to the terms and conditions of the Mulan PSL v2.
  * You may obtain a copy of Mulan PSL v2 at:
  *          http://license.coscl.org.cn/MulanPSL2
  *
  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
  * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
  * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
  *
  * See the Mulan PSL v2 for more details.
  ***************************************************************************************/

package xiangshan.mem

import org.chipsalliance.cde.config.Parameters
import chisel3._
import chisel3.util._
import utils._
import utility._
import xiangshan.ExceptionNO._
import xiangshan._
import xiangshan.backend.rob.RobPtr
import xiangshan.backend.Bundles._
import xiangshan.mem._
import xiangshan.backend.fu.vector.Bundles._
import xiangshan.backend.fu.FuConfig._
import xiangshan.backend.fu.FuType


class VSplitPipeline(isVStore: Boolean = false)(implicit p: Parameters) extends VLSUModule{
  val io = IO(new VSplitPipelineIO(isVStore))
  // will be override later
  def us_whole_reg(fuOpType: UInt): Bool = false.B
  def us_mask(fuOpType: UInt): Bool = false.B
  def us_fof(fuOpType: UInt): Bool = false.B
  //TODO vdIdxReg should no longer be useful, don't delete it for now
  val vdIdxReg = RegInit(0.U(3.W))

  val s1_ready = WireInit(false.B)
  io.in.ready := s1_ready

  /**-----------------------------------------------------------
    * s0 stage
    * decode and generate AlignedType, uop mask, preIsSplit
    * ----------------------------------------------------------
    */
  val s0_uop = io.in.bits.uop
  val s0_vtype = s0_uop.vpu.vtype
  val s0_sew = s0_vtype.vsew
  val s0_eew = s0_uop.vpu.veew
  val s0_lmul = s0_vtype.vlmul
  // when load whole register or unit-stride masked , emul should be 1
  val s0_fuOpType = s0_uop.fuOpType
  val s0_mop = s0_fuOpType(6, 5)
  val s0_nf = Mux(us_whole_reg(s0_fuOpType), 0.U, s0_uop.vpu.nf)
  val s0_vm = s0_uop.vpu.vm
  val s0_emul = Mux(us_whole_reg(s0_fuOpType) ,GenUSWholeEmul(s0_uop.vpu.nf), Mux(us_mask(s0_fuOpType), 0.U(mulBits.W), EewLog2(s0_eew) - s0_sew + s0_lmul))
  val s0_preIsSplit = !isUnitStride(s0_mop)
  val s0_nfield        = s0_nf +& 1.U

  val s0_valid         = Wire(Bool())
  val s0_kill          = io.in.bits.uop.robIdx.needFlush(io.redirect)
  val s0_can_go        = s1_ready
  val s0_fire          = s0_valid && s0_can_go
  val s0_out           = Wire(new VLSBundle(isVStore))

  val isUsWholeReg = isUnitStride(s0_mop) && us_whole_reg(s0_fuOpType)
  val isMaskReg = isUnitStride(s0_mop) && us_mask(s0_fuOpType)
  val isSegment = s0_nf =/= 0.U && !us_whole_reg(s0_fuOpType)
  val instType = Cat(isSegment, s0_mop)
  val uopIdx = io.in.bits.uop.vpu.vuopIdx
  val uopIdxInField = GenUopIdxInField(instType, s0_emul, s0_lmul, uopIdx)
  val vdIdxInField = GenVdIdxInField(instType, s0_emul, s0_lmul, uopIdxInField)
  val lmulLog2 = Mux(s0_lmul.asSInt >= 0.S, 0.U, s0_lmul)
  val emulLog2 = Mux(s0_emul.asSInt >= 0.S, 0.U, s0_emul)
  val numEewLog2 = emulLog2 - EewLog2(s0_eew)
  val numSewLog2 = lmulLog2 - s0_sew
  val numFlowsSameVdLog2 = Mux(
    isIndexed(instType),
    log2Up(VLENB).U - s0_sew(1,0),
    log2Up(VLENB).U - s0_eew
  )
  // numUops = nf * max(lmul, emul)
  val lmulLog2Pos = Mux(s0_lmul.asSInt < 0.S, 0.U, s0_lmul)
  val emulLog2Pos = Mux(s0_emul.asSInt < 0.S, 0.U, s0_emul)
  val numUops = Mux(
    isIndexed(s0_mop) && s0_lmul.asSInt > s0_emul.asSInt,
    (s0_nf +& 1.U) << lmulLog2Pos,
    (s0_nf +& 1.U) << emulLog2Pos
  )

  val vvl = io.in.bits.src_vl.asTypeOf(VConfig()).vl
  val evl = Mux(isUsWholeReg,
                GenUSWholeRegVL(io.in.bits.uop.vpu.nf +& 1.U, s0_eew),
                Mux(isMaskReg,
                    GenUSMaskRegVL(vvl),
                    vvl))
  val vvstart = io.in.bits.uop.vpu.vstart
  val alignedType = Mux(isIndexed(instType), s0_sew(1, 0), s0_eew)
  val broadenAligendType = Mux(s0_preIsSplit, Cat("b0".U, alignedType), "b100".U) // if is unit-stride, use 128-bits memory access
  val flowsLog2 = GenRealFlowLog2(instType, s0_emul, s0_lmul, s0_eew, s0_sew)
  val flowsPrevThisUop = (uopIdxInField << flowsLog2).asUInt // # of flows before this uop in a field
  val flowsPrevThisVd = (vdIdxInField << numFlowsSameVdLog2).asUInt // # of flows before this vd in a field
  val flowsIncludeThisUop = ((uopIdxInField +& 1.U) << flowsLog2).asUInt // # of flows before this uop besides this uop
  val flowNum = io.in.bits.flowNum.get
  // max index in vd, only use in index instructions for calculate index
  val maxIdxInVdIndex = GenVLMAX(Mux(s0_emul.asSInt > 0.S, 0.U, s0_emul), s0_eew)
  val indexVlMaxInVd = GenVlMaxMask(maxIdxInVdIndex, elemIdxBits)

  // For vectore indexed  instructions:
  //  When emul is greater than lmul, multiple uop correspond to a Vd, e.g:
  //    vsetvli	t1,t0,e8,m1,ta,ma    lmul = 1
  //    vluxei16.v	v2,(a0),v8       emul = 2
  //    In this case, we need to ensure the flownumis right shift by flowsPrevThisUop, However, the mask passed to mergebuff is right shift by flowsPrevThisVd e.g:
  //      vl = 9
  //      srcMask = 0x1FF
  //      uopIdxInField = 0 and vdIdxInField = 0, flowMask = 0x00FF, toMergeBuffMask = 0x01FF
  //      uopIdxInField = 1 and vdIdxInField = 0, flowMask = 0x0001, toMergeBuffMask = 0x01FF
  //      uopIdxInField = 0 and vdIdxInField = 0, flowMask = 0x0000, toMergeBuffMask = 0x0000
  //      uopIdxInField = 0 and vdIdxInField = 0, flowMask = 0x0000, toMergeBuffMask = 0x0000
  val isSpecialIndexed = isIndexed(instType) && s0_emul.asSInt > s0_lmul.asSInt

  val srcMask = GenFlowMask(Mux(s0_vm, Fill(VLEN, 1.U(1.W)), io.in.bits.src_mask), vvstart, evl, true)
  val srcMaskShiftBits = Mux(isSpecialIndexed, flowsPrevThisUop, flowsPrevThisVd)

  val flowMask = ((srcMask &
    UIntToMask(flowsIncludeThisUop.asUInt, VLEN + 1) &
    (~UIntToMask(flowsPrevThisUop.asUInt, VLEN)).asUInt
  ) >> srcMaskShiftBits)(VLENB - 1, 0)
  val indexedSrcMask = (srcMask >> flowsPrevThisVd).asUInt //only for index instructions

  // Used to calculate the element index.
  // See 'splitbuffer' for 'io.out.splitIdxOffset' and 'mergebuffer' for 'merge data'
  val indexedSplitOffset = Mux(isSpecialIndexed, flowsPrevThisUop - flowsPrevThisVd, 0.U) // only for index instructions of emul > lmul
  val vlmax = GenVLMAX(s0_lmul, s0_sew)

  // connect
  s0_out := DontCare
  s0_out match {case x =>
    x.uop := io.in.bits.uop
    x.uop.imm := 0.U
    x.uop.vpu.vl := evl
    x.uop.uopIdx := uopIdx
    x.uop.numUops := numUops
    x.uop.lastUop := (uopIdx +& 1.U) === numUops
    x.uop.vpu.nf  := s0_nf
    x.rawNf := io.in.bits.uop.vpu.nf
    x.flowMask := flowMask
    x.indexedSrcMask := indexedSrcMask // Only vector indexed instructions uses it
    x.indexedSplitOffset := indexedSplitOffset
    x.byteMask := GenUopByteMask(flowMask, Cat("b0".U, alignedType))(VLENB - 1, 0)
    x.fof := isUnitStride(s0_mop) && us_fof(s0_fuOpType)
    x.baseAddr := io.in.bits.src_rs1
    x.stride := io.in.bits.src_stride
    x.flowNum := flowNum
    x.nfields := s0_nfield
    x.vm := s0_vm
    x.usWholeReg := isUsWholeReg
    x.usMaskReg := isMaskReg
    x.eew := s0_eew
    x.sew := s0_sew
    x.emul := s0_emul
    x.lmul := s0_lmul
    x.vlmax := Mux(isUsWholeReg, evl, vlmax)
    x.instType := instType
    x.data := io.in.bits.src_vs3
    x.vdIdxInField := vdIdxInField
    x.preIsSplit  := s0_preIsSplit
    x.alignedType := broadenAligendType
    x.indexVlMaxInVd := indexVlMaxInVd
  }
  s0_valid := io.in.valid && !s0_kill
  /**-------------------------------------
    * s1 stage
    * ------------------------------------
    * generate UopOffset
    */
  val s1_valid         = RegInit(false.B)
  val s1_kill          = Wire(Bool())
  val s1_in            = Wire(new VLSBundle(isVStore))
  val s1_can_go        = io.out.ready && io.toMergeBuffer.req.ready
  val s1_fire          = s1_valid && !s1_kill && s1_can_go

  s1_ready         := s1_kill || !s1_valid || s1_can_go

  when(s0_fire){
    s1_valid := true.B
  }.elsewhen(s1_fire){
    s1_valid := false.B
  }.elsewhen(s1_kill){
    s1_valid := false.B
  }
  s1_in := RegEnable(s0_out, s0_fire)

  val s1_flowNum          = s1_in.flowNum
  val s1_uop              = s1_in.uop
  val s1_uopidx           = s1_uop.vpu.vuopIdx
  val s1_nf               = s1_uop.vpu.nf
  val s1_nfields          = s1_in.nfields
  val s1_eew              = s1_in.eew
  val s1_emul             = s1_in.emul
  val s1_lmul             = s1_in.lmul
  val s1_instType         = s1_in.instType
  val s1_stride           = s1_in.stride
  val s1_vmask            = FillInterleaved(8, s1_in.byteMask)(VLEN-1, 0)
  val s1_alignedType      = s1_in.alignedType
  val s1_isSpecialIndexed = isIndexed(s1_instType) && s1_emul.asSInt > s1_lmul.asSInt
  val s1_mask             = Mux(s1_isSpecialIndexed, s1_in.indexedSrcMask, s1_in.flowMask)
  val s1_vdIdx            = s1_in.vdIdxInField
  val s1_fof              = s1_in.fof
  val s1_notIndexedStride = Mux( // stride for strided/unit-stride instruction
    isStrided(s1_instType),
    s1_stride(XLEN - 1, 0), // for strided load, stride = x[rs2]
    s1_nfields << s1_eew // for unit-stride load, stride = eew * NFIELDS
  )

  val stride            = Mux(isIndexed(s1_instType), s1_stride, s1_notIndexedStride).asUInt // if is index instructions, get index when split
  val uopOffset         = genVUopOffset(s1_instType, s1_fof, s1_uopidx, s1_nf, s1_eew, stride, s1_alignedType)
  // for Unit-Stride, if uop's addr is aligned with 128-bits, split it to one flow, otherwise split two
  val usLowBitsAddr     = getCheckAddrLowBits(s1_in.baseAddr, maxMemByteNum) + getCheckAddrLowBits(uopOffset, maxMemByteNum)
  val usMask            = Cat(0.U(VLENB.W), s1_in.byteMask) << getCheckAddrLowBits(usLowBitsAddr, maxMemByteNum)
  val usAligned128      = getCheckAddrLowBits(usLowBitsAddr, maxMemByteNum) === 0.U // addr 128-bit aligned
  val usMaskLowActive   = genUSSplitMask(usMask.asTypeOf(UInt(32.W)), 1.U).asUInt.orR
  val usMaskHighActive  = genUSSplitMask(usMask.asTypeOf(UInt(32.W)), 0.U).asUInt.orR
  val usActiveNum       = Mux(
                            usMaskLowActive && usMaskHighActive,
                            VecMemUnitStrideMaxFlowNum.U,
                            Mux(usMaskLowActive || usMaskHighActive, (VecMemUnitStrideMaxFlowNum - 1).U, 0.U)
                          )

  val activeNum         = Mux(s1_in.preIsSplit, PopCount(s1_in.flowMask), usActiveNum)


  s1_kill               := s1_in.uop.robIdx.needFlush(io.redirect)

  // query mergeBuffer
  io.toMergeBuffer.req.valid             := io.out.ready && s1_valid// only can_go will get MergeBuffer entry
  io.toMergeBuffer.req.bits.flowNum      := activeNum
  io.toMergeBuffer.req.bits.data         := s1_in.data
  io.toMergeBuffer.req.bits.uop          := s1_in.uop
  io.toMergeBuffer.req.bits.uop.vpu.nf   := s1_in.rawNf
  io.toMergeBuffer.req.bits.mask         := s1_mask
  io.toMergeBuffer.req.bits.vaddr        := s1_in.baseAddr
  io.toMergeBuffer.req.bits.vdIdx        := s1_vdIdx  //TODO vdIdxReg should no longer be useful, don't delete it for now
  io.toMergeBuffer.req.bits.fof          := s1_in.fof
  io.toMergeBuffer.req.bits.vlmax        := s1_in.vlmax
//   io.toMergeBuffer.req.bits.vdOffset :=

  //TODO vdIdxReg should no longer be useful, don't delete it for now
//  when (s1_in.uop.lastUop && s1_fire || s1_kill) {
//    vdIdxReg := 0.U
//  }.elsewhen(s1_fire) {
//    vdIdxReg := vdIdxReg + 1.U
//    XSError(vdIdxReg + 1.U === 0.U, s"Overflow! The number of vd should be less than 8\n")
//  }
  // out connect
  io.out.valid          := s1_valid && io.toMergeBuffer.resp.valid && (activeNum =/= 0.U) // if activeNum == 0, this uop do nothing, can be killed.
  io.out.bits           := s1_in
  io.out.bits.uopOffset := uopOffset
  io.out.bits.uopAddr   := s1_in.baseAddr + uopOffset
  io.out.bits.stride    := stride
  io.out.bits.mBIndex   := io.toMergeBuffer.resp.bits.mBIndex
  io.out.bits.usLowBitsAddr := usLowBitsAddr
  io.out.bits.usAligned128  := usAligned128
  io.out.bits.usMask        := usMask
  io.out.bits.uop.vpu.nf    := s1_in.rawNf

  XSPerfAccumulate("split_out",     io.out.fire)
  XSPerfAccumulate("pipe_block",    io.out.valid && !io.out.ready)
  XSPerfAccumulate("mbuffer_block", s1_valid && io.out.ready && !io.toMergeBuffer.resp.valid)
}

abstract class VSplitBuffer(isVStore: Boolean = false)(implicit p: Parameters) extends VLSUModule{
  val io = IO(new VSplitBufferIO(isVStore))
  lazy val fuCfg    = if(isVStore) VstuCfg else VlduCfg

  val uopq          = Reg(new VLSBundle(isVStore))
  val allocated     = RegInit(false.B)
  val needCancel    = WireInit(false.B)
  val activeIssue   = Wire(Bool())
  val inActiveIssue = Wire(Bool())
  val splitFinish   = WireInit(false.B)

  // for split
  val splitIdx = RegInit(0.U(flowIdxBits.W))
  val strideOffsetReg = RegInit(0.U(VLEN.W))

  /**
    * Redirect
    */
  val cancelEnq    = io.in.bits.uop.robIdx.needFlush(io.redirect)
  val canEnqueue   = io.in.valid
  val needEnqueue  = canEnqueue && !cancelEnq

  // enqueue
  val offset    = PopCount(needEnqueue)
  val canAccept = !allocated || allocated && splitFinish && (activeIssue || inActiveIssue) // if is valid entry, need split finish and send last uop
  io.in.ready  := canAccept
  val doEnqueue = canAccept && needEnqueue

  when(doEnqueue){
    uopq := io.in.bits
  }

  //split uops
  val issueValid       = allocated && !needCancel
  val issueEntry       = uopq
  val issueMbIndex     = issueEntry.mBIndex
  val issueFlowNum     = issueEntry.flowNum
  val issueBaseAddr    = issueEntry.baseAddr
  val issueUopAddr     = issueEntry.uopAddr
  val issueUop         = issueEntry.uop
  val issueUopIdx      = issueUop.vpu.vuopIdx
  val issueInstType    = issueEntry.instType
  val issueUopOffset   = issueEntry.uopOffset
  val issueEew         = issueEntry.eew
  val issueSew         = issueEntry.sew
  val issueLmul        = issueEntry.lmul
  val issueEmul        = issueEntry.emul
  val issueAlignedType = issueEntry.alignedType
  val issuePreIsSplit  = issueEntry.preIsSplit
  val issueByteMask    = issueEntry.byteMask
  val issueUsMask      = issueEntry.usMask
  val issueVLMAXMask   = issueEntry.vlmax - 1.U
  val issueIsWholeReg  = issueEntry.usWholeReg
  val issueVLMAXLog2   = GenVLMAXLog2(issueEntry.lmul, issueSew)
  val issueVlMaxInVd   = issueEntry.indexVlMaxInVd
  val issueUsLowBitsAddr = issueEntry.usLowBitsAddr
  val issueUsAligned128  = issueEntry.usAligned128
  val elemIdx = GenElemIdx(
    instType = issueInstType,
    emul = issueEmul,
    lmul = issueLmul,
    eew = issueEew,
    sew = issueSew,
    uopIdx = issueUopIdx,
    flowIdx = splitIdx
  ) // elemIdx inside an inst, for exception

  val splitIdxOffset = issueEntry.indexedSplitOffset + splitIdx

  val indexFlowInnerIdx = elemIdx & issueVlMaxInVd
  val nfIdx = Mux(issueIsWholeReg, 0.U, elemIdx >> issueVLMAXLog2)
  val fieldOffset = nfIdx << issueAlignedType // field offset inside a segment

  val indexedStride    = IndexAddr( // index for indexed instruction
    index = issueEntry.stride,
    flow_inner_idx = indexFlowInnerIdx,
    eew = issueEew
  )
  val issueStride = Mux(isIndexed(issueInstType), indexedStride, strideOffsetReg)
  val vaddr = issueUopAddr + issueStride
  val mask = genVWmask128(vaddr ,issueAlignedType) // scala maske for flow
  val flowMask = issueEntry.flowMask
  /*
   * Unit-Stride split to one flow or two flow.
   * for Unit-Stride, if uop's addr is aligned with 128-bits, split it to one flow, otherwise split two
   */
  val usSplitMask      = genUSSplitMask(issueUsMask, splitIdx)
  val usMaskInSingleUop = (genUSSplitMask(issueUsMask, 1.U) === 0.U) // if second splited Mask is zero, means this uop is unnecessary to split
  val usNoSplit        = (issueUsAligned128 || usMaskInSingleUop) &&
                          !issuePreIsSplit &&
                          (splitIdx === 0.U)// unit-stride uop don't need to split into two flow
  val usSplitVaddr     = genUSSplitAddr(issueUopAddr, splitIdx, XLEN)
  val regOffset        = getCheckAddrLowBits(issueUsLowBitsAddr, maxMemByteNum) // offset in 256-bits vd
  XSError((splitIdx > 1.U && usNoSplit) || (splitIdx > 1.U && !issuePreIsSplit) , "Unit-Stride addr split error!\n")

  val vecActive = Mux(!issuePreIsSplit, usSplitMask.orR, (flowMask & UIntToOH(splitIdx)).orR)
  // no-unit-stride can trigger misalign
  val addrAligned = LookupTree(issueEew, List(
    "b00".U   -> true.B,                //b
    "b01".U   -> (vaddr(0)    === 0.U), //h
    "b10".U   -> (vaddr(1, 0) === 0.U), //w
    "b11".U   -> (vaddr(2, 0) === 0.U)  //d
  )) || !issuePreIsSplit

  // data
  io.out.bits match { case x =>
    x.uop                   := issueUop
    x.uop.imm               := 0.U
    x.uop.exceptionVec      := ExceptionNO.selectByFu(issueUop.exceptionVec, fuCfg)
    x.vaddr                 := Mux(!issuePreIsSplit, usSplitVaddr, vaddr)
    x.basevaddr             := issueBaseAddr
    x.alignedType           := issueAlignedType
    x.isvec                 := true.B
    x.mask                  := Mux(!issuePreIsSplit, usSplitMask, mask)
    x.reg_offset            := regOffset //for merge unit-stride data
    x.vecActive             := vecActive // currently, unit-stride's flow always send to pipeline
    x.is_first_ele          := DontCare
    x.usSecondInv           := usNoSplit
    x.elemIdx               := elemIdx
    x.elemIdxInsideVd       := splitIdxOffset // if is Unit-Stride, elemIdx is the index of 2 splited mem request (for merge data)
    x.uop_unit_stride_fof   := DontCare
    x.isFirstIssue          := DontCare
    x.mBIndex               := issueMbIndex
  }

  // redirect
  needCancel := uopq.uop.robIdx.needFlush(io.redirect) && allocated

 /* Execute logic */
  /** Issue to scala pipeline**/

  lazy val misalignedCanGo = true.B
  val allowIssue = (addrAligned || misalignedCanGo) && io.out.ready
  val issueCount = Mux(usNoSplit, 2.U, (PopCount(inActiveIssue) + PopCount(activeIssue))) // for dont need split unit-stride, issue two flow
  splitFinish := splitIdx >= (issueFlowNum - issueCount)

  // handshake
  activeIssue := issueValid && allowIssue && vecActive // active issue, current use in no unit-stride
  inActiveIssue := issueValid && !vecActive
  when (!issueEntry.uop.robIdx.needFlush(io.redirect)) {
    when (!splitFinish) {
      when (activeIssue || inActiveIssue) {
        // The uop has not been entirly splited yet
        splitIdx := splitIdx + issueCount
        strideOffsetReg := Mux(!issuePreIsSplit, 0.U, strideOffsetReg + issueEntry.stride) // when normal unit-stride, don't use strideOffsetReg
      }
    }.otherwise {
      when (activeIssue || inActiveIssue) {
        // The uop is done spliting
        splitIdx := 0.U(flowIdxBits.W) // initialize flowIdx
        strideOffsetReg := 0.U
      }
    }
  }.otherwise {
    splitIdx := 0.U(flowIdxBits.W) // initialize flowIdx
    strideOffsetReg := 0.U
  }
  // allocated
  when(doEnqueue){ // if enqueue need to been cancelled, it will be false, so this have high priority
    allocated := true.B
  }.elsewhen(needCancel) { // redirect
    allocated := false.B
  }.elsewhen(splitFinish && (activeIssue || inActiveIssue)){ //dequeue
    allocated := false.B
  }

  // out connect
  io.out.valid := issueValid && vecActive && (addrAligned || misalignedCanGo) // TODO: inactive unit-stride uop do not send to pipeline

  XSPerfAccumulate("out_valid",             io.out.valid)
  XSPerfAccumulate("out_fire",              io.out.fire)
  XSPerfAccumulate("out_fire_unitstride",   io.out.fire && !issuePreIsSplit)
  XSPerfAccumulate("unitstride_vlenAlign",  io.out.fire && !issuePreIsSplit && getCheckAddrLowBits(io.out.bits.vaddr, maxMemByteNum) === 0.U)
  XSPerfAccumulate("unitstride_invalid",    io.out.ready && issueValid && !issuePreIsSplit && PopCount(io.out.bits.mask).orR)
}

class VSSplitBufferImp(implicit p: Parameters) extends VSplitBuffer(isVStore = true){
  override lazy val misalignedCanGo = io.vstdMisalign.get.storePipeEmpty && io.vstdMisalign.get.storeMisalignBufferEmpty

  // split data
  val splitData = genVSData(
        data = issueEntry.data.asUInt,
        elemIdx = splitIdxOffset,
        alignedType = issueAlignedType
      )
  val flowData = genVWdata(splitData, issueAlignedType)
  val usSplitData      = genUSSplitData(issueEntry.data.asUInt, splitIdx, vaddr(3,0))

  val sqIdx = issueUop.sqIdx + splitIdx
  io.out.bits.uop.sqIdx := sqIdx
  io.out.bits.uop.exceptionVec(storeAddrMisaligned) := !addrAligned && !issuePreIsSplit && io.out.bits.mask.orR

  // send data to sq
  val vstd = io.vstd.get
  vstd.valid := issueValid && (vecActive || !issuePreIsSplit)
  vstd.bits.uop := issueUop
  vstd.bits.uop.sqIdx := sqIdx
  vstd.bits.uop.fuType := FuType.vstu.U
  vstd.bits.data := Mux(!issuePreIsSplit, usSplitData, flowData)
  vstd.bits.debug := DontCare
  vstd.bits.vecDebug.get := DontCare // maybe assign later
  vstd.bits.vdIdx.get := DontCare
  vstd.bits.vdIdxInField.get := DontCare
  vstd.bits.isFromLoadUnit   := DontCare
  vstd.bits.mask.get := Mux(!issuePreIsSplit, usSplitMask, mask)

  if(env.EnableDifftest){
    val usVaddrOffset   = LookupTree(issueEew, List(
      "b00".U -> 0.U,
      "b01".U -> vaddr(0),
      "b10".U -> vaddr(1, 0),
      "b11".U -> vaddr(2, 0)
    ))

    vstd.bits.vecDebug.get.start  := Mux(splitIdx === 0.U, usVaddrOffset, 0.U)// for unaligned store event
    vstd.bits.vecDebug.get.offset := usVaddrOffset
  }

}

class VLSplitBufferImp(implicit p: Parameters) extends VSplitBuffer(isVStore = false){
  io.out.bits.uop.lqIdx := issueUop.lqIdx + splitIdx
  io.out.bits.uop.exceptionVec(loadAddrMisaligned) := !addrAligned && !issuePreIsSplit && io.out.bits.mask.orR
  io.out.bits.uop.fuType := FuType.vldu.U
}

class VSSplitPipelineImp(implicit p: Parameters) extends VSplitPipeline(isVStore = true){
  override def us_whole_reg(fuOpType: UInt): Bool = fuOpType === VstuType.vsr
  override def us_mask(fuOpType: UInt): Bool      = fuOpType === VstuType.vsm
  override def us_fof(fuOpType: UInt): Bool       = false.B // dont have vector fof store
}

class VLSplitPipelineImp(implicit p: Parameters) extends VSplitPipeline(isVStore = false){

  override def us_whole_reg(fuOpType: UInt): Bool = fuOpType === VlduType.vlr
  override def us_mask(fuOpType: UInt): Bool      = fuOpType === VlduType.vlm
  override def us_fof(fuOpType: UInt): Bool       = fuOpType === VlduType.vleff
}

class VLSplitImp(implicit p: Parameters) extends VLSUModule{
  val io = IO(new VSplitIO(isVStore=false))
  val splitPipeline = Module(new VLSplitPipelineImp())
  val splitBuffer = Module(new VLSplitBufferImp())
  val mergeBufferNack = io.threshold.get.valid && io.threshold.get.bits =/= io.in.bits.uop.lqIdx
  // Split Pipeline
  splitPipeline.io.in <> io.in
  io.in.ready := splitPipeline.io.in.ready && !mergeBufferNack
  splitPipeline.io.redirect <> io.redirect
  io.toMergeBuffer <> splitPipeline.io.toMergeBuffer

  // skid buffer
  skidBuffer(splitPipeline.io.out, splitBuffer.io.in,
    Mux(splitPipeline.io.out.fire,
      splitPipeline.io.out.bits.uop.robIdx.needFlush(io.redirect),
      splitBuffer.io.in.bits.uop.robIdx.needFlush(io.redirect)),
    "VSSplitSkidBuffer")

  // Split Buffer
  splitBuffer.io.redirect <> io.redirect
  io.out <> splitBuffer.io.out
}

class VSSplitImp(implicit p: Parameters) extends VLSUModule{
  val io = IO(new VSplitIO(isVStore=true))
  val splitPipeline = Module(new VSSplitPipelineImp())
  val splitBuffer = Module(new VSSplitBufferImp())
  // Split Pipeline
  splitPipeline.io.in <> io.in
  splitPipeline.io.redirect <> io.redirect
  io.toMergeBuffer <> splitPipeline.io.toMergeBuffer

  // skid buffer
  skidBuffer(splitPipeline.io.out, splitBuffer.io.in,
    Mux(splitPipeline.io.out.fire,
      splitPipeline.io.out.bits.uop.robIdx.needFlush(io.redirect),
      splitBuffer.io.in.bits.uop.robIdx.needFlush(io.redirect)),
    "VSSplitSkidBuffer")

  // Split Buffer
  splitBuffer.io.redirect <> io.redirect
  io.out <> splitBuffer.io.out
  io.vstd.get <> splitBuffer.io.vstd.get

  io.vstdMisalign.get <> splitBuffer.io.vstdMisalign.get
}

