package xiangshan.backend.fu.fpu.divsqrt

import chisel3._
import chisel3.util._
import utils._
import xiangshan.backend.fu.fpu._
import xiangshan.backend.fu.fpu.util.FPUDebug

class OnTheFlyConv(len: Int) extends Module {
  val io = IO(new Bundle() {
    val resetSqrt = Input(Bool())
    val resetDiv = Input(Bool())
    val enable = Input(Bool())
    val qi = Input(SInt(3.W))
    val QM = Output(UInt(len.W))
    val Q = Output(UInt(len.W))
    val F = Output(UInt(len.W))
  })
  val Q, QM = Reg(UInt(len.W))

  /**  FGen:
    *  use additional regs to avoid
    *  big width shifter since FGen is in cirtical path
    */
  val mask = Reg(SInt(len.W))
  val b_111, b_1100 = Reg(UInt(len.W))
  when(io.resetSqrt){
    mask := Cat("b1".U(1.W), 0.U((len-1).W)).asSInt()
    b_111 := "b111".U(3.W) << (len-5)
    b_1100 := "b1100".U(4.W) << (len-5)
  }.elsewhen(io.enable){
    mask := mask >> 2
    b_111 := b_111 >> 2
    b_1100 := b_1100 >> 2
  }
  val b_00, b_01, b_10, b_11 = Reg(UInt((len-3).W))
  b_00 := 0.U
  when(io.resetDiv || io.resetSqrt){
    b_01 := Cat("b01".U(2.W), 0.U((len-5).W))
    b_10 := Cat("b10".U(2.W), 0.U((len-5).W))
    b_11 := Cat("b11".U(2.W), 0.U((len-5).W))
  }.elsewhen(io.enable){
    b_01 := b_01 >> 2
    b_10 := b_10 >> 2
    b_11 := b_11 >> 2
  }

  val negQ = ~Q
  val sqrtToCsaMap = Seq(
    1 -> (negQ, b_111),
    2 -> (negQ, b_1100),
    -1 -> (QM, b_111),
    -2 -> (QM, b_1100)
  ).map(
    m => m._1.S(3.W).asUInt() ->
      ( ((m._2._1 << Mux(io.qi(0), 1.U, 2.U)).asUInt() & (mask >> io.qi(0)).asUInt()) | m._2._2 )
  )
  val sqrtToCsa = MuxLookup(io.qi.asUInt(), 0.U, sqrtToCsaMap)

  val Q_load_00 = Q | b_00
  val Q_load_01 = Q | b_01
  val Q_load_10 = Q | b_10
  val QM_load_01 = QM | b_01
  val QM_load_10 = QM | b_10
  val QM_load_11 = QM | b_11

  when(io.resetSqrt){
    Q := Cat(1.U(3.W), 0.U((len-3).W))
    QM := 0.U
  }.elsewhen(io.resetDiv){
    Q := 0.U
    QM := 0.U
  }.elsewhen(io.enable){
    val QConvMap = Seq(
      0 -> Q_load_00,
      1 -> Q_load_01,
      2 -> Q_load_10,
      -1 -> QM_load_11,
      -2 -> QM_load_10
    ).map(m => m._1.S(3.W).asUInt() -> m._2)
    val QMConvMap = Seq(
      0 -> QM_load_11,
      1 -> Q_load_00,
      2 -> Q_load_01,
      -1 -> QM_load_10,
      -2 -> QM_load_01
    ).map(m => m._1.S(3.W).asUInt() -> m._2)
    Q := MuxLookup(io.qi.asUInt(), DontCare, QConvMap)
    QM := MuxLookup(io.qi.asUInt(), DontCare, QMConvMap)
  }

  io.F := sqrtToCsa
  io.QM := QM
  io.Q := Q

  FPUDebug(){
    when(io.enable){
      printf(p"[on the fly conv] q:${io.qi} A:${Binary(Q)} B:${Binary(QM)} \n")
    }
  }
}
