package xiangshan.backend.fu.vector

import org.chipsalliance.cde.config.Parameters
import chisel3._
import chisel3.util._
import xiangshan.XSBundle
import xiangshan.XSCoreParamsKey
import xiangshan.backend.decode.isa.bitfield.InstVType
import xiangshan.backend.fu.VtypeStruct
import _root_.utils.NamedUInt
import utility.ZeroExt

object Bundles {

  /**
    * vtype bundle, should not used as csr reg
    */
  class VType(implicit p: Parameters) extends Bundle {
    val illegal = Bool()
    val vma     = Bool()
    val vta     = Bool()
    val vsew    = VSew()
    val vlmul   = VLmul()
  }

  /**
    * vset module's vtype bundle, use 3 bits vsew to check if it is illegal
    *
    * we need to get 3 bits vsew in Vtype struct, then vset module can check if it is reserved.
    * and we use 2 bits to store vsew in other places to save space
    */
  class VsetVType(implicit p: Parameters) extends XSBundle {
    val illegal  = Bool()
    val reserved = UInt((XLEN - 9).W)
    val vma      = Bool()
    val vta      = Bool()
    val vsew     = VtypeVSew()
    val vlmul    = VLmul()
  }

  object VType {
    def apply()(implicit p: Parameters) : VType = {
      new VType
    }

    def fromInstVType(instVType: InstVType)(implicit p: Parameters) : VType = {
      val res = Wire(VType())
      res.vma   := instVType.vma
      res.vta   := instVType.vta
      res.vsew  := instVType.vsew(VSew.width - 1, 0)
      res.vlmul := instVType.vlmul
      res.illegal := false.B // Todo: add illegal check function
      res
    }

    def fromVtypeStruct(vtypeStruct: VtypeStruct)(implicit p: Parameters): VType = {
      val res = Wire(VType())
      res.illegal := vtypeStruct.vill
      res.vma := vtypeStruct.vma
      res.vta := vtypeStruct.vta
      res.vsew := vtypeStruct.vsew(VSew.width - 1, 0)
      res.vlmul := vtypeStruct.vlmul
      res
    }

    def toVtypeStruct(vtype: VType)(implicit p: Parameters) : VtypeStruct = {
      val res = WireInit(0.U.asTypeOf(new VtypeStruct))
      res.vill := vtype.illegal
      res.vma := vtype.vma
      res.vta := vtype.vta
      res.vsew := Cat(0.U(1.W), vtype.vsew)
      res.vlmul := vtype.vlmul
      res
    }

    def initVtype()(implicit p: Parameters) : VType = {
      val res = Wire(VType())
      res.illegal := true.B
      res.vma := false.B
      res.vta := false.B
      res.vsew := 0.U
      res.vlmul := 0.U
      res
    }

    def mu: UInt = 0.U(1.W)

    def ma: UInt = 1.U(1.W)

    def tu: UInt = 0.U(1.W)

    def ta: UInt = 1.U(1.W)
  }

  object VsetVType {
    def apply()(implicit p: Parameters) : VsetVType = {
      new VsetVType
    }

    def fromInstVType(instVType: InstVType)(implicit p: Parameters) : VsetVType = {
      val res = Wire(VsetVType())
      res.vma   := instVType.vma
      res.vta   := instVType.vta
      res.vsew  := instVType.vsew
      res.vlmul := instVType.vlmul
      res.illegal := false.B
      res.reserved := instVType.reserved
      res
    }

    def fromVtypeStruct(vtypeStruct: VtypeStruct)(implicit p: Parameters): VsetVType = {
      val res = Wire(VsetVType())
      res.illegal := vtypeStruct.vill
      res.vma := vtypeStruct.vma
      res.vta := vtypeStruct.vta
      res.vsew := vtypeStruct.vsew
      res.vlmul := vtypeStruct.vlmul
      res.reserved := vtypeStruct.reserved
      res
    }
  }

  class VConfig(implicit p: Parameters) extends Bundle {
    val vtype = new VType
    val vl    = Vl()
  }

  object VConfig {
    def apply()(implicit p: Parameters) : VConfig = {
      new VConfig()
    }
  }

  // modify the width when support more vector data width
  object VSew extends NamedUInt(2) {
    def e8  : UInt = "b000".U(width.W)
    def e16 : UInt = "b001".U(width.W)
    def e32 : UInt = "b010".U(width.W)
    def e64 : UInt = "b011".U(width.W)

    def reserved: BitPat = BitPat("b1??")

    def isReserved(sew: UInt) : Bool = {
      require(sew.getWidth >= 2 && sew.getWidth <= 3)
      if (sew.getWidth == 3) {
        sew === reserved
      } else {
        false.B
      }
    }
  }

  object SewOH extends NamedUInt(4) {
    def e8  : UInt = "b0001".U(width.W)
    def e16 : UInt = "b0010".U(width.W)
    def e32 : UInt = "b0100".U(width.W)
    def e64 : UInt = "b1000".U(width.W)

    def convertFromVSew(vsew: UInt): UInt = {
      require(vsew.getWidth >= 2 && vsew.getWidth <= 3)
      ZeroExt(UIntToOH(vsew), this.width)
    }
  }

  object VtypeVSew extends NamedUInt(3)

  object VLmul extends NamedUInt(3) {
    def m1  : UInt = "b000".U(width.W)
    def m2  : UInt = "b001".U(width.W)
    def m4  : UInt = "b010".U(width.W)
    def m8  : UInt = "b011".U(width.W)
    def mf2 : UInt = "b111".U(width.W)
    def mf4 : UInt = "b110".U(width.W)
    def mf8 : UInt = "b101".U(width.W)

    def reserved: BitPat = BitPat("b100")

    def isReserved(vlmul: UInt) : Bool = {
      require(vlmul.getWidth == 3)
      vlmul === reserved
    }

    def makeNoLessThanM1(uint: UInt): UInt = {
      checkInputWidth(uint)
      Mux(uint(2), m1, uint)
    }
  }

  object Vl {
    def apply()(implicit p: Parameters): UInt = UInt(width.W)

    def width(implicit p: Parameters) = p(XSCoreParamsKey).vlWidth
  }

  object Vstart {
    def apply()(implicit p: Parameters): UInt = UInt(width.W)

    def width(implicit p: Parameters) = p(XSCoreParamsKey).vlWidth - 1
  }

  object Vxsat extends NamedUInt(1)

  object Vxrm extends NamedUInt(2)

  object Nf extends NamedUInt(3)

  object VEew extends NamedUInt(2)

  object NumLsElem {
    def apply()(implicit p: Parameters): UInt = UInt(width.W)

    def width(implicit p: Parameters) = log2Up(p(XSCoreParamsKey).maxElemPerVreg) + 1
  }

  class Fpu extends Bundle{
    val isFpToVecInst = Bool()
    val isFP32Instr   = Bool()
    val isFP64Instr   = Bool()
    val isReduction   = Bool()
    val isFoldTo1_2   = Bool()
    val isFoldTo1_4   = Bool()
    val isFoldTo1_8   = Bool()
  }
  object Fpu {
    def apply() = new Fpu
  }
}
