/***************************************************************************************
  * 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._
import xiangshan.backend.Bundles._
import xiangshan.backend.fu.NewCSR.CsrTriggerBundle
import xiangshan.backend.rob.RobPtr
import xiangshan.backend.fu.PMPRespBundle
import xiangshan.backend.fu.vector.Bundles._
import xiangshan.mem.Bundles._
import xiangshan.cache.mmu.{TlbCmd, TlbRequestIO}
import xiangshan.cache._

class VLSBundle(isVStore: Boolean=false)(implicit p: Parameters) extends VLSUBundle {
  val flowMask            = UInt(VLENB.W) // each bit for a flow
  val byteMask            = UInt(VLENB.W) // each bit for a byte
  val data                = UInt(VLEN.W)
  // val fof            = Bool() // fof is only used for vector loads
  val excp_eew_index      = UInt(elemIdxBits.W)
  // val exceptionVec   = ExceptionVec() // uop has exceptionVec
  val baseAddr            = UInt(XLEN.W)
  val uopAddr             = UInt(XLEN.W)
  val stride              = UInt(VLEN.W)
  // val flow_counter = UInt(flowIdxBits.W)

  // instruction decode result
  val flowNum             = UInt(flowIdxBits.W) // # of flows in a uop
  // val flowNumLog2 = UInt(log2Up(flowIdxBits).W) // log2(flowNum), for better timing of multiplication
  val nfields             = UInt(fieldBits.W) // NFIELDS
  val vm                  = Bool() // whether vector masking is enabled
  val usWholeReg          = Bool() // unit-stride, whole register load
  val usMaskReg           = Bool() // unit-stride, masked store/load
  val eew                 = VEew() // size of memory elements
  val sew                 = UInt(ewBits.W)
  val emul                = UInt(mulBits.W)
  val lmul                = UInt(mulBits.W)
  val vlmax               = UInt(elemIdxBits.W)
  val instType            = UInt(3.W)
  val vd_last_uop         = Bool()
  val vd_first_uop        = Bool()

  // Because the back-end needs to handle exceptions, it is necessary to retain the original NF.
  // So we choose to pass the original value in the pipeline and override it when out.
  val rawNf                = Nf()
  val indexedSrcMask     = UInt(VLENB.W)
  val indexedSplitOffset  = UInt(flowIdxBits.W)
  // Inst's uop
  val uop                 = new DynInst

  val fof                 = Bool()
  val vdIdxInField        = UInt(log2Up(maxMUL).W)
  val uopOffset           = UInt(VLEN.W)
  val preIsSplit          = Bool() // if uop need split, only not Unit-Stride or not 128bit-aligned unit stride need split
  val mBIndex             = if(isVStore) UInt(vsmBindexBits.W) else UInt(vlmBindexBits.W)

  val alignedType         = UInt(alignTypeBits.W)
  val indexVlMaxInVd      = UInt(elemIdxBits.W)

  val usLowBitsAddr       = UInt((log2Up(maxMemByteNum)).W)
  val usAligned128        = Bool()
  val usMask              = UInt((VLENB*2).W) // for unit-stride split
}

object VSFQFeedbackType {
  val tlbMiss = 0.U(3.W)
  val mshrFull = 1.U(3.W)
  val dataInvalid = 2.U(3.W)
  val bankConflict = 3.U(3.W)
  val ldVioCheckRedo = 4.U(3.W)
  val feedbackInvalid = 7.U(3.W)

  def apply() = UInt(3.W)
}

class VSFQFeedback (implicit p: Parameters) extends XSBundle {
  // val flowPtr = new VsFlowPtr
  val hit   = Bool()
  //val flushState = Bool()
  val sourceType = VSFQFeedbackType()
  //val dataInvalidSqIdx = new SqPtr
  val paddr = UInt(PAddrBits.W)
  val mmio = Bool()
  val atomic = Bool()
  val exceptionVec = ExceptionVec()
}

class VecPipelineFeedbackIO(isVStore: Boolean=false) (implicit p: Parameters) extends VLSUBundle {
  val mBIndex              = if(isVStore) UInt(vsmBindexBits.W) else UInt(vlmBindexBits.W)
  val hit                  = Bool()
  val isvec                = Bool()
  val flushState           = Bool()
  val sourceType           = VSFQFeedbackType()
  val trigger              = TriggerAction()
  //val dataInvalidSqIdx = new SqPtr
  //val paddr                = UInt(PAddrBits.W)
  val nc                   = Bool()
  val mmio                 = Bool()
  //val atomic               = Bool()
  val exceptionVec         = ExceptionVec()
  val hasException         = Bool() // Active
  val vaddr                = UInt(XLEN.W)
  val vaNeedExt            = Bool()
  val gpaddr               = UInt(XLEN.W)
  val isForVSnonLeafPTE    = Bool()
  val vstart               = UInt(elemIdxBits.W)
  val vecTriggerMask       = UInt((VLEN/8).W)

  //val vec                  = new OnlyVecExuOutput
   // feedback
  val vecFeedback          = Bool()

  val usSecondInv          = Bool() // only for unit stride, second flow is Invalid
  val elemIdx              = UInt(elemIdxBits.W) // element index
  val mask                 = UInt(VLENB.W)
  val alignedType          = UInt(alignTypeBits.W)
  // for load
  val reg_offset           = OptionWrapper(!isVStore, UInt(vOffsetBits.W))
  val elemIdxInsideVd      = OptionWrapper(!isVStore, UInt(elemIdxBits.W)) // element index in scope of vd
  val vecdata              = OptionWrapper(!isVStore, UInt(VLEN.W))
}

class VecPipeBundle(isVStore: Boolean=false)(implicit p: Parameters) extends VLSUBundle {
  val vaddr               = UInt(XLEN.W)
  val basevaddr           = UInt(VAddrBits.W)
  val mask                = UInt(VLENB.W)
  val isvec               = Bool()
  val uop_unit_stride_fof = Bool()
  val reg_offset          = UInt(vOffsetBits.W)
  val alignedType         = UInt(alignTypeBits.W)
  val vecActive           = Bool() // 1: vector active element, 0: vector not active element
  val is_first_ele        = Bool()
  val isFirstIssue        = Bool()

  val uop = new DynInst

  val usSecondInv         = Bool() // only for unit stride, second flow is Invalid
  val mBIndex             = if(isVStore) UInt(vsmBindexBits.W) else UInt(vlmBindexBits.W)
  val elemIdx             = UInt(elemIdxBits.W)
  val elemIdxInsideVd     = UInt(elemIdxBits.W) // only use in unit-stride
}

object VecFeedbacks {
  // need to invalid lsq entry
  val FLUSH  = 0
  // merge buffer commits one uop
  val COMMIT  = 1
  // last uop of an inst, sq can commit
  val LAST = 2
  // total feedbacks
  val allFeedbacks = 3
}

class MergeBufferReq(isVStore: Boolean=false)(implicit p: Parameters) extends VLSUBundle{
  val mask                = UInt(VLENB.W)
  val vaddr               = UInt(VAddrBits.W)
  val flowNum             = UInt(flowIdxBits.W)
  val uop                 = new DynInst
  val data                = UInt(VLEN.W)
  val vdIdx               = UInt(3.W)
  val fof                 = Bool()
  val vlmax               = UInt(elemIdxBits.W)
  // val vdOffset            = UInt(vdOffset.W)
}

class MergeBufferResp(isVStore: Boolean=false)(implicit p: Parameters) extends VLSUBundle{
  val mBIndex             = if(isVStore) UInt(vsmBindexBits.W) else UInt(vlmBindexBits.W)
  val fail                = Bool()
}

class ToMergeBufferIO(isVStore: Boolean=false)(implicit p: Parameters) extends VLSUBundle{
  val req                 = DecoupledIO(new MergeBufferReq(isVStore))
  val resp                = Flipped(ValidIO(new MergeBufferResp(isVStore)))
}

class FromSplitIO(isVStore: Boolean=false)(implicit p: Parameters) extends VLSUBundle{
  val req                 = Flipped(DecoupledIO(new MergeBufferReq(isVStore)))
  val resp                = ValidIO(new MergeBufferResp(isVStore))
  // val issueInactive       = Flipped(ValidIO())
}

class FeedbackToSplitIO(implicit p: Parameters) extends VLSUBundle{
  val threshold            = Bool()
}

class FeedbackToLsqIO(implicit p: Parameters) extends VLSUBundle{
  val robidx = new RobPtr
  val uopidx = UopIdx()
  val vaddr = UInt(XLEN.W)
  val vaNeedExt = Bool()
  val gpaddr = UInt(GPAddrBits.W)
  val isForVSnonLeafPTE = Bool()
  val feedback = Vec(VecFeedbacks.allFeedbacks, Bool())
    // for exception
  val vstart           = UInt(elemIdxBits.W)
  val vl               = UInt(elemIdxBits.W)
  val exceptionVec     = ExceptionVec()

  def isFlush  = feedback(VecFeedbacks.FLUSH)
  def isCommit = feedback(VecFeedbacks.COMMIT)
  def isLast = feedback(VecFeedbacks.LAST)
}

class storeMisaignIO(implicit p: Parameters) extends Bundle{
  val storePipeEmpty           = Input(Bool())
  val storeMisalignBufferEmpty = Input(Bool())
}

class VSplitIO(isVStore: Boolean=false)(implicit p: Parameters) extends VLSUBundle{
  val redirect            = Flipped(ValidIO(new Redirect))
  val in                  = Flipped(Decoupled(new MemExuInput(isVector = true))) // from iq
  val toMergeBuffer       = new ToMergeBufferIO(isVStore) //to merge buffer req mergebuffer entry
  val out                 = Decoupled(new VecPipeBundle(isVStore))// to scala pipeline
  val vstd                = OptionWrapper(isVStore, Valid(new MemExuOutput(isVector = true)))
  val vstdMisalign        = OptionWrapper(isVStore, new storeMisaignIO)
  val threshold            = OptionWrapper(!isVStore, Flipped(ValidIO(new LqPtr)))
}

class VSplitPipelineIO(isVStore: Boolean=false)(implicit p: Parameters) extends VLSUBundle{
  val redirect            = Flipped(ValidIO(new Redirect))
  val in                  = Flipped(Decoupled(new MemExuInput(isVector = true)))
  val toMergeBuffer       = new ToMergeBufferIO(isVStore) // req mergebuffer entry, inactive elem issue
  val out                 = Decoupled(new VLSBundle())// to split buffer
}

class VSplitBufferIO(isVStore: Boolean=false)(implicit p: Parameters) extends VLSUBundle{
  val redirect            = Flipped(ValidIO(new Redirect))
  val in                  = Flipped(Decoupled(new VLSBundle()))
  val out                 = Decoupled(new VecPipeBundle(isVStore))//to scala pipeline
  val vstd                = OptionWrapper(isVStore, ValidIO(new MemExuOutput(isVector = true)))
  val vstdMisalign        = OptionWrapper(isVStore, new storeMisaignIO)
}

class VMergeBufferIO(isVStore : Boolean=false)(implicit p: Parameters) extends VLSUBundle{
  val redirect            = Flipped(ValidIO(new Redirect))
  val fromPipeline        = if(isVStore) Vec(StorePipelineWidth, Flipped(DecoupledIO(new VecPipelineFeedbackIO(isVStore)))) else Vec(LoadPipelineWidth, Flipped(DecoupledIO(new VecPipelineFeedbackIO(isVStore))))
  val fromSplit           = if(isVStore) Vec(VecStorePipelineWidth, new FromSplitIO) else Vec(VecLoadPipelineWidth, new FromSplitIO) // req mergebuffer entry, inactive elem issue
  val uopWriteback        = if(isVStore) Vec(VSUopWritebackWidth, DecoupledIO(new MemExuOutput(isVector = true))) else Vec(VLUopWritebackWidth, DecoupledIO(new MemExuOutput(isVector = true)))
  val toSplit             = OptionWrapper(!isVStore, new FeedbackToSplitIO())
  val toLsq               = if(isVStore) Vec(VSUopWritebackWidth, ValidIO(new FeedbackToLsqIO)) else Vec(VLUopWritebackWidth, ValidIO(new FeedbackToLsqIO)) // for lsq deq
  val feedback            = if(isVStore) Vec(VSUopWritebackWidth, ValidIO(new RSFeedback(isVector = true))) else Vec(VLUopWritebackWidth, ValidIO(new RSFeedback(isVector = true)))//for rs replay

  val fromMisalignBuffer  = OptionWrapper(isVStore, Flipped(new StoreMaBufToVecStoreMergeBufferIO))
}

class VSegmentUnitIO(implicit p: Parameters) extends VLSUBundle{
  val in                  = Flipped(Decoupled(new MemExuInput(isVector = true))) // from iq
  val uopwriteback        = DecoupledIO(new MemExuOutput(isVector = true)) // writeback data
  val csrCtrl             = Flipped(new CustomCSRCtrlIO)
  val rdcache             = new DCacheLoadIO // read dcache port
  val sbuffer             = Decoupled(new DCacheWordReqWithVaddrAndPfFlag)
  val vecDifftestInfo     = Decoupled(new ToSbufferDifftestInfoBundle) // to sbuffer
  val dtlb                = new TlbRequestIO(2)
  val pmpResp             = Flipped(new PMPRespBundle())
  val flush_sbuffer       = new SbufferFlushBundle
  val feedback            = ValidIO(new RSFeedback(isVector = true))
  val redirect            = Flipped(ValidIO(new Redirect))
  val exceptionInfo       = ValidIO(new FeedbackToLsqIO)
  //trigger
  val fromCsrTrigger      = Input(new CsrTriggerBundle)
}

class VfofDataBuffIO(implicit p: Parameters) extends VLSUBundle{
  val redirect            = Flipped(ValidIO(new Redirect))
  val in                  = Vec(VecLoadPipelineWidth, Flipped(Decoupled(new MemExuInput(isVector=true))))
  val mergeUopWriteback   = Vec(VLUopWritebackWidth, Flipped(DecoupledIO(new FeedbackToLsqIO)))

  val uopWriteback        = DecoupledIO(new MemExuOutput(isVector = true))
}
