package xiangshan.backend.decode

import org.chipsalliance.cde.config.Parameters
import chisel3._
import chisel3.util._
import xiangshan._
import xiangshan.backend.fu.vector.Bundles.{VType, VsetVType}
import xiangshan.backend.decode.isa.bitfield.{InstVType, Riscv32BitInst, XSInstBitFields}
import xiangshan.backend.fu.VsetModule

/**
 * IO of VTypeGen
 */
class VTypeGenIO(implicit p: Parameters) extends XSBundle {
  val insts = Flipped(Vec(DecodeWidth, ValidIO(UInt(32.W))))
  val walkToArchVType = Input(Bool())
  val walkVType   = Flipped(Valid(new VType))
  val canUpdateVType = Input(Bool())
  val vtype = Output(new VType)
  val vsetvlVType = Input(new VType)
  val commitVType = new Bundle {
    val vtype = Flipped(Valid(new VType))
    val hasVsetvl = Input(Bool())
  }
}
/**
 * Vtype is the vector configuration register, which is the main part of vector extension.
 * It contains the vector length and the vector mask. This class is a module that generates vtype for each instruction.
 */
class VTypeGen(implicit p: Parameters) extends XSModule{
  val io = IO(new VTypeGenIO)
  /** valid signals of instructions */
  private val instValidVec = io.insts.map(_.valid)
  /** instructions code */
  private val instFieldVec = io.insts.map(_.bits.asTypeOf(new XSInstBitFields))
  // Only check vsetvli and vsetivli here.
  // vsetvl will flush pipe, need not to generate new vtype in decode stage.
  private val isVsetVec = VecInit(instFieldVec.map(fields =>
    (fields.OPCODE === "b1010111".U) && (fields.WIDTH === "b111".U) && (
      fields.ALL(31) === "b0".U ||
      fields.ALL(31, 30) === "b11".U
    )
  ).zip(instValidVec).map { case (isVset, valid) => valid && isVset})

  /** one-hot encoded first vset instruction's location */
  private val firstVsetOH: Vec[Bool] = VecInit(PriorityEncoderOH(isVsetVec))
  /** instruction field of first vset instruction */
  private val firstVsetInstField: XSInstBitFields = PriorityMux(firstVsetOH, instFieldVec)

  /** first vset instruction is a vsetvli */
  private val isVsetvli= (firstVsetInstField.OPCODE === "b1010111".U) &&
    (firstVsetInstField.WIDTH === "b111".U) && 
    (firstVsetInstField.ALL(31) === "b0".U)

  /** register of vtype in architectural state */
  private val vtypeArch = RegInit(VType.initVtype())
  /** register of vtype in speculative state */
  private val vtypeSpec = RegInit(VType.initVtype())

  /** next register of vtype in architectural state */
  private val vtypeArchNext = WireInit(vtypeArch)
  /** next register of vtype in architectural state */
  private val vtypeSpecNext = WireInit(vtypeSpec)

  vtypeArch := vtypeArchNext
  vtypeSpec := vtypeSpecNext

  /** New vtype field set by new vset instruction. Set vtype according to instruction type of vset */
  private val instVType: InstVType = Mux(isVsetvli, firstVsetInstField.ZIMM_VSETVLI.asTypeOf(new InstVType),
    firstVsetInstField.ZIMM_VSETIVLI.asTypeOf(new InstVType))
  /** Convert InstVType to VsetVType */
  private val vtypei: VsetVType = VsetVType.fromInstVType(instVType)

  /** New vtype configuration set by new vset instruction. */
  private val vsetModule = Module(new VsetModule)
  vsetModule.io.in.avl := 0.U
  vsetModule.io.in.vtype := vtypei
  vsetModule.io.in.func := VSETOpType.uvsetvcfg_xi

  /** New vtype generated by vsetModule */
  private val vtypeNew = vsetModule.io.out.vconfig.vtype

  /** Set the source of vtypeArch from commit instructions */
  when(io.commitVType.hasVsetvl) {
    vtypeArchNext := io.vsetvlVType
  }.elsewhen(io.commitVType.vtype.valid) {
    vtypeArchNext := io.commitVType.vtype.bits
  }

  /** whether there is a vset instruction among input instructions */
  private val inHasVset = isVsetVec.asUInt.orR

  /**
   * Set the source of vtypeSpec from the following sources:
   * 1. committed vsetvl instruction, which flushes the pipeline.
   * 2. walk-vtype, which is used to update vtype when walking.
   * 3. walking to architectural vtype
   * 4. new vset instruction
   */
  when(io.commitVType.hasVsetvl) {
    // when vsetvl instruction commit, also update vtypeSpec, because vsetvl flush pipe
    vtypeSpecNext := io.vsetvlVType
  }.elsewhen(io.walkVType.valid) {
    vtypeSpecNext := io.walkVType.bits
  }.elsewhen(io.walkToArchVType) {
    vtypeSpecNext := vtypeArch
  }.elsewhen(inHasVset && io.canUpdateVType) {
    vtypeSpecNext := vtypeNew
  }

  /** pass speculative vtype to output */
  io.vtype := vtypeSpec

  // just make verilog more readable
  dontTouch(isVsetVec)
}
