package xiangshan.backend.fu.wrapper

import org.chipsalliance.cde.config.Parameters
import chisel3._
import utility.{LookupTree, SignExt, ZeroExt}
import xiangshan.MULOpType
import xiangshan.backend.fu.{ArrayMulDataModule, FuncUnit, HasPipelineReg, MulDivCtrl}
import xiangshan.backend.fu.FuConfig

class MulUnit(cfg: FuConfig)(implicit p: Parameters) extends FuncUnit(cfg)
  with HasPipelineReg
{
  override def latency: Int = 2

  private val xlen = cfg.destDataBits

  val func = io.in.bits.ctrl.fuOpType
  val src = io.in.bits.data.src

  val op = MULOpType.getOp(func)

  private val mdCtrl = Wire(new MulDivCtrl)
  mdCtrl.isW := MULOpType.isW(func)
  mdCtrl.isHi := MULOpType.isH(func)
  mdCtrl.sign := DontCare

  val sext = SignExt(_: UInt, xlen + 1)
  val zext = ZeroExt(_: UInt, xlen + 1)
  val mulInputCvtTable: Seq[(UInt, (UInt => UInt, UInt => UInt))] = List(
    MULOpType.getOp(MULOpType.mul)    -> (zext, zext),
    MULOpType.getOp(MULOpType.mulh)   -> (sext, sext),
    MULOpType.getOp(MULOpType.mulhsu) -> (sext, zext),
    MULOpType.getOp(MULOpType.mulhu)  -> (zext, zext),
    MULOpType.getOp(MULOpType.mulw7)  -> (_.asUInt(6, 0), zext),
  )

  // len should be xlen + 1
  private val len = cfg.destDataBits + 1
  private val mulDataModule = Module(new ArrayMulDataModule(len))

  mulDataModule.io.a := LookupTree(
    op,
    mulInputCvtTable.map { case (k, v) => (k, v._1(src(0))) }
  )

  mulDataModule.io.b := LookupTree(
    op,
    mulInputCvtTable.map { case (k, v) => (k, v._2(src(1))) }
  )

  mulDataModule.io.regEnables := VecInit((1 to latency) map (i => regEnable(i)))
  private val result = mulDataModule.io.result

  private var mdCtrlVec = Seq(mdCtrl)
  for (i <- 1 to latency) {
    mdCtrlVec = mdCtrlVec :+ PipelineReg(i)(mdCtrlVec(i - 1))
  }
  private val res = Mux(mdCtrlVec.last.isHi, result(2 * xlen - 1, xlen), result(xlen - 1, 0))

  io.out.bits.res.data := Mux(mdCtrlVec.last.isW, SignExt(res(31, 0), xlen), res)
}
