package xiangshan.backend.fu.NewCSR

import chisel3._
import chisel3.util._
import xiangshan.ExceptionNO
import xiangshan.backend.fu.NewCSR.CSRBundles.{CauseBundle, PrivState, XtvecBundle}
import xiangshan.backend.fu.NewCSR.CSRDefines.XtvecMode
import xiangshan.backend.fu.NewCSR.CSRBundleImplicitCast._


class TrapHandleModule extends Module {
  val io = IO(new TrapHandleIO)

  private val trapInfo = io.in.trapInfo
  private val privState = io.in.privState
  private val mstatus  = io.in.mstatus
  private val vsstatus = io.in.vsstatus
  private val mnstatus = io.in.mnstatus
  private val mideleg = io.in.mideleg.asUInt
  private val hideleg = io.in.hideleg.asUInt
  private val medeleg = io.in.medeleg.asUInt
  private val hedeleg = io.in.hedeleg.asUInt
  private val mvien = io.in.mvien.asUInt
  private val hvien = io.in.hvien.asUInt

  private val hasTrap = trapInfo.valid
  private val hasNMI = hasTrap && trapInfo.bits.nmi
  private val hasIR = hasTrap && trapInfo.bits.isInterrupt
  private val hasEX = hasTrap && !trapInfo.bits.isInterrupt

  private val exceptionVec = io.in.trapInfo.bits.trapVec
  private val intrVec = io.in.trapInfo.bits.intrVec
  private val hasEXVec = Mux(hasEX, exceptionVec, 0.U)
  private val hasIRVec = Mux(hasIR, intrVec, 0.U)

  private val irToHS = io.in.trapInfo.bits.irToHS
  private val irToVS = io.in.trapInfo.bits.irToVS

  private val highestPrioEXVec = Wire(Vec(64, Bool()))
  highestPrioEXVec.zipWithIndex.foreach { case (excp, i) =>
    if (ExceptionNO.priorities.contains(i)) {
      val higherEXSeq = ExceptionNO.getHigherExcpThan(i)
      excp := (
        higherEXSeq.nonEmpty.B && Cat(higherEXSeq.map(num => !hasEXVec(num))).andR ||
        higherEXSeq.isEmpty.B
      ) && hasEXVec(i)
    } else
      excp := false.B
  }

  private val highestPrioIR  = hasIRVec.asUInt
  private val highestPrioEX  = highestPrioEXVec.asUInt

  private val mEXVec  = highestPrioEX
  private val hsEXVec = highestPrioEX & medeleg
  private val vsEXVec = highestPrioEX & medeleg & hedeleg

  // nmi handle in MMode only and default handler is mtvec
  private val  mHasIR = hasIR
  private val hsHasIR = hasIR && irToHS & !hasNMI
  private val vsHasIR = hasIR && irToVS & !hasNMI

  private val  mHasEX =  mEXVec.orR
  private val hsHasEX = hsEXVec.orR
  private val vsHasEX = vsEXVec.orR

  private val  mHasTrap =  mHasEX ||  mHasIR
  private val hsHasTrap = hsHasEX || hsHasIR
  private val vsHasTrap = vsHasEX || vsHasIR

  private val handleTrapUnderHS = !privState.isModeM && hsHasTrap
  private val handleTrapUnderVS = privState.isVirtual && vsHasTrap
  private val handleTrapUnderM = !handleTrapUnderVS && !handleTrapUnderHS

  // Todo: support more interrupt and exception
  private val exceptionRegular = OHToUInt(highestPrioEX)
  private val interruptNO = highestPrioIR
  private val exceptionNO = Mux(trapInfo.bits.singleStep, ExceptionNO.breakPoint.U, exceptionRegular)

  private val causeNO = Mux(hasIR, interruptNO, exceptionNO)

  // sm/ssdbltrp
  private val m_EX_DT  = handleTrapUnderM  && mstatus.MDT.asBool  && hasTrap
  private val s_EX_DT  = handleTrapUnderHS && mstatus.SDT.asBool  && hasTrap
  private val vs_EX_DT = handleTrapUnderVS && vsstatus.SDT.asBool && hasTrap

  private val dbltrpToMN = m_EX_DT && mnstatus.NMIE.asBool // NMI not allow double trap
  private val hasDTExcp  = m_EX_DT || s_EX_DT || vs_EX_DT

  private val trapToHS = handleTrapUnderHS && !s_EX_DT && !vs_EX_DT
  private val traptoVS = handleTrapUnderVS && !vs_EX_DT

  private val xtvec = MuxCase(io.in.mtvec, Seq(
    traptoVS -> io.in.vstvec,
    trapToHS -> io.in.stvec
  ))
  private val adjustinterruptNO = Mux(
    InterruptNO.getVS.map(_.U === interruptNO).reduce(_ || _) && vsHasIR,
    interruptNO - 1.U, // map VSSIP, VSTIP, VSEIP to SSIP, STIP, SEIP
    interruptNO,
  )
  private val pcFromXtvec = Cat(xtvec.addr.asUInt + Mux(xtvec.mode === XtvecMode.Vectored && hasIR, adjustinterruptNO(5, 0), 0.U), 0.U(2.W))

  io.out.entryPrivState := MuxCase(default = PrivState.ModeM, mapping = Seq(
    traptoVS -> PrivState.ModeVS,
    trapToHS -> PrivState.ModeHS,
  ))

  io.out.causeNO.Interrupt := hasIR
  io.out.causeNO.ExceptionCode := causeNO
  io.out.pcFromXtvec := pcFromXtvec
  io.out.hasDTExcp := hasDTExcp
  io.out.dbltrpToMN := dbltrpToMN

}

class TrapHandleIO extends Bundle {
  val in = Input(new Bundle {
    val trapInfo = ValidIO(new Bundle {
      val trapVec = UInt(64.W)
      val nmi = Bool()
      val intrVec = UInt(8.W)
      val isInterrupt = Bool()
      val singleStep = Bool()
      // trap to x mode
      val irToHS = Bool()
      val irToVS = Bool()
    })
    val privState = new PrivState
    val mstatus = new MstatusBundle
    val vsstatus = new SstatusBundle
    val mnstatus = new MnstatusBundle
    val mideleg = new MidelegBundle
    val medeleg = new MedelegBundle
    val hideleg = new HidelegBundle
    val hedeleg = new HedelegBundle
    val mvien = new MvienBundle
    val hvien = new HvienBundle
    // trap vector
    val mtvec = Input(new XtvecBundle)
    val stvec = Input(new XtvecBundle)
    val vstvec = Input(new XtvecBundle)
  })

  val out = new Bundle {
    val entryPrivState = new PrivState
    val causeNO = new CauseBundle
    val dbltrpToMN = Bool()
    val hasDTExcp = Bool()
    val pcFromXtvec = UInt(64.W)
  }
}
