package xiangshan.backend.fu.NewCSR

import chisel3._
import chisel3.util._
import freechips.rocketchip.rocket.CSRs
import org.chipsalliance.cde.config.Parameters
import utility.ZeroExt
import xiangshan.backend.fu.NewCSR.CSRBundles._
import xiangshan.backend.fu.NewCSR.CSRConfig._
import xiangshan.backend.fu.NewCSR.CSRDefines.{CSRROField => RO, CSRRWField => RW, _}
import xiangshan.backend.fu.NewCSR.CSREnumTypeImplicitCast._
import xiangshan.backend.fu.NewCSR.CSREvents.{SretEventSinkBundle, TrapEntryHSEventSinkBundle}
import xiangshan.backend.fu.NewCSR.CSRFunc._
import xiangshan.backend.fu.NewCSR.ChiselRecordForField._
import system.HasSoCParameter

import scala.collection.immutable.SeqMap

trait HypervisorLevel { self: NewCSR =>

  val hstatus = Module(new HstatusModule)
    .setAddr(CSRs.hstatus)

  val hedeleg = Module(new CSRModule("Hedeleg", new HedelegBundle))
    .setAddr(CSRs.hedeleg)

  val hideleg = Module(new CSRModule("Hideleg", new HidelegBundle)
    with HasIpIeBundle
  {
    regOut := reg & mideleg
    regOut.getLocal.zip(reg.getLocal).zip(mideleg.getLocal).zip(mvien.getLocal).foreach {
      case (((regOutLCI, regLCI), midelegLCI), mvienLCI) =>
        regOutLCI := regLCI && (midelegLCI || mvienLCI)
    }
  })
    .setAddr(CSRs.hideleg)

  val hie = Module(new CSRModule("Hie", new HieBundle)
    with HasIpIeBundle
    with HypervisorBundle
  {
    val toMie = IO(new HieToMie)

    val mieIsAlias = mideleg

    bundle.getFields.map(_.lsb).foreach { num =>
      val wtMie  = toMie.getByNum(num)
      wtMie.specifyField(
        _.valid := wen && mieIsAlias(num) && wtMie.bits.isRW.B,
        _.bits  := wen && mieIsAlias(num) && wtMie.bits.isRW.B &< wdata(num),
      )

      regOut(num) := mieIsAlias(num) && wtMie.bits.isRW.B &< mie(num)
    }
  })
    .setAddr(CSRs.hie)

  val htimedelta = Module(new CSRModule("Htimedelta", new Htimedelta))
    .setAddr(CSRs.htimedelta)

  val hcounteren = Module(new CSRModule("Hcounteren", new Counteren))
    .setAddr(CSRs.hcounteren)

  val hgeie = Module(new CSRModule("Hgeie", new HgeieBundle))
    .setAddr(CSRs.hgeie)

  val hvien = Module(new CSRModule("Hvien", new HvienBundle))
    .setAddr(CSRs.hvien)

  val hvictl = Module(new CSRModule("Hvictl", new HvictlBundle))
    .setAddr(CSRs.hvictl)

  val henvcfg = Module(new CSRModule("Henvcfg", new HEnvCfg) with HasHypervisorEnvBundle {
    when(!menvcfg.STCE) {
      regOut.STCE := 0.U
    }
    when(!menvcfg.PBMTE) {
      regOut.PBMTE := 0.U
    }
    when(!menvcfg.DTE) {
      regOut.DTE := 0.U
    }
  }).setAddr(CSRs.henvcfg)

  val htval = Module(new CSRModule("Htval", new XtvalBundle) with TrapEntryHSEventSinkBundle)
    .setAddr(CSRs.htval)

  val hip = Module(new CSRModule("Hip", new HipBundle)
    with HypervisorBundle
    with HasExternalInterruptBundle
    with HasIpIeBundle
  {
    val toHvip = IO(new HipToHvip)

    // hip.VSEIP is read-only alias of mip.VSEIP, mip.VSEIP := hvip.VSEIP|hgeip(VGEIN)|platIR.VSEIP
    // hip.VSTIP is read-only alias of mip.VSTIP, mip.VSTIP := hvip.VSTIP|time+htimedelta>=vstimecmp
    // hip.SGEIP is read-only alias of mip.SGEIP, mip.SGEIP := |(hgeip&hgeie)
    regOut.VSTIP := mip.VSTIP
    regOut.VSEIP := mip.VSEIP
    regOut.SGEIP := mip.SGEIP

    // hip.VSSIP is alias of hvip.VSSIP, writable
    toHvip.VSSIP.valid := wen
    toHvip.VSSIP.bits  := wdata.VSSIP
    regOut.VSSIP := this.hvip.VSSIP
    // vsip.SSIP is alias of hip.VSSIP, so vsip.SSIP is alias of hvip.VSSIP.
    // vsip.SSIP write throuth to hvip.VSSIP
  })
    .setAddr(CSRs.hip)

  val hvip = Module(new CSRModule("Hvip", new HvipBundle) {
    val fromMip  = IO(Flipped(new MipToHvip))
    val fromHip  = IO(Flipped(new HipToHvip))
    val fromVSip = IO(Flipped(new VSipToHvip))

    when (fromMip.VSSIP.valid || fromHip.VSSIP.valid || fromVSip.VSSIP.valid) {
      reg.VSSIP := Mux1H(Seq(
        fromMip.VSSIP.valid -> fromMip.VSSIP.bits,
        fromHip.VSSIP.valid -> fromHip.VSSIP.bits,
        fromVSip.VSSIP.valid -> fromVSip.VSSIP.bits,
      ))
    }

    reg.getLocal zip fromVSip.getLocal foreach { case (rLCIP, vsipLCIP) =>
      // sip should assert valid when hideleg=0 && hvien=1
      when(vsipLCIP.valid) {
        rLCIP := vsipLCIP.bits
      }
    }
  })
    .setAddr(CSRs.hvip)

  val hviprio1 = Module(new CSRModule("Hviprio1", new Hviprio1Bundle))
    .setAddr(CSRs.hviprio1)

  val hviprio2 = Module(new CSRModule("Hviprio2", new Hviprio2Bundle))
    .setAddr(CSRs.hviprio2)

  val htinst = Module(new CSRModule("Htinst", new XtinstBundle) with TrapEntryHSEventSinkBundle)
    .setAddr(CSRs.htinst)

  val hgatp = Module(new CSRModule("Hgatp", new HgatpBundle) {
    // Ref: 13.2.10. Hypervisor Guest Address Translation and Protection Register (hgatp)
    // A write to hgatp with an unsupported MODE value is not ignored as it is for satp. Instead, the fields of
    // hgatp are WARL in the normal way, when so indicated.

    // The length of ppn is 44 bits.
    // make PPN[1:0] read-only zero.
    val ppnMask = ZeroExt((Fill(PPNLength - 2, 1.U(1.W)) ## 0.U(2.W)).take(PAddrBits - PageOffsetWidth), PPNLength)

    when (wen) {
      reg.VMID := wdata.VMID
      reg.PPN  := wdata.PPN & ppnMask
      when (wdata.MODE.isLegal) {
        reg.MODE := wdata.MODE
      }.otherwise {
        reg.MODE := reg.MODE
      }
    }.otherwise {
      reg := reg
    }
  })
    .setAddr(CSRs.hgatp)

  val hgeip = Module(new CSRModule("Hgeip", new HgeipBundle) with HasAIABundle {
    regOut.ip := aiaToCSR.vseip
  })
    .setAddr(CSRs.hgeip)

  val hstateen0 = Module(new CSRModule("Hstateen0", new Hstateen0Bundle) with HasStateenBundle {
    // For every bit in an mstateen CSR that is zero (whether read-only zero or set to zero), the same bit
    // appears as read-only zero in the matching hstateen and sstateen CSRs.
    regOut := reg.asUInt & fromMstateen0.asUInt
  }).setAddr(CSRs.hstateen0)

  val hstateen1 = Module(new CSRModule("Hstateen1", new HstateenNonZeroBundle) with HasStateenBundle {
    regOut := reg.asUInt & fromMstateen1.asUInt
  }).setAddr(CSRs.hstateen1)

  val hstateen2 = Module(new CSRModule("Hstateen2", new HstateenNonZeroBundle) with HasStateenBundle {
    regOut := reg.asUInt & fromMstateen2.asUInt
  }).setAddr(CSRs.hstateen2)

  val hstateen3 = Module(new CSRModule("Hstateen3", new HstateenNonZeroBundle) with HasStateenBundle {
    regOut := reg.asUInt & fromMstateen3.asUInt
  }).setAddr(CSRs.hstateen3)

  val hcontext = Module(new CSRModule("Hcontext", new McontextBundle) {
    val fromMcontext = IO(Input(new McontextBundle))
    val toMcontext   = IO(ValidIO(new McontextBundle))
    toMcontext.valid := wen
    toMcontext.bits.HCONTEXT := wdata.HCONTEXT.asUInt
    regOut.HCONTEXT := fromMcontext.HCONTEXT.asUInt
  }).setAddr(CSRs.hcontext)

  val hypervisorCSRMods: Seq[CSRModule[_]] = Seq(
    hstatus,
    hedeleg,
    hideleg,
    hie,
    htimedelta,
    hcounteren,
    hgeie,
    hvien,
    hvictl,
    henvcfg,
    htval,
    hip,
    hvip,
    hviprio1,
    hviprio2,
    htinst,
    hgatp,
    hgeip,
    hstateen0,
    hstateen1,
    hstateen2,
    hstateen3,
    hcontext,
  )

  val hypervisorCSRMap: SeqMap[Int, (CSRAddrWriteBundle[_], UInt)] = SeqMap.from(
    hypervisorCSRMods.map(csr => (csr.addr -> (csr.w -> csr.rdata))).iterator
  )

  val hypervisorCSROutMap: SeqMap[Int, UInt] = SeqMap.from(
    hypervisorCSRMods.map(csr => (csr.addr -> csr.regOut.asInstanceOf[CSRBundle].asUInt)).iterator
  )
}

class HstatusBundle extends CSRBundle {

  val VSBE  = RO(5).withReset(0.U)
  val GVA   = RW(6)
  val SPV   = VirtMode(7)
  val SPVP  = RW(8)
  val HU    = RW(9)
  val VGEIN = HstatusVgeinField(17, 12, wNoFilter, rNoFilter).withReset(0.U)
  val VTVM  = RW(20).withReset(0.U)
  val VTW   = RW(21).withReset(0.U)
  val VTSR  = RW(22).withReset(0.U)
  val VSXL  = XLENField(33, 32).withReset(XLENField.XLEN64)
  val HUPMM = EnvPMM(49, 48, wNoEffect).withReset(EnvPMM.Disable) // Ssnpm extension

}

object HstatusVgeinField extends CSREnum with WLRLApply

class HstatusModule(implicit p: Parameters) extends CSRModule("Hstatus", new HstatusBundle)
  with SretEventSinkBundle
  with TrapEntryHSEventSinkBundle

class HvipBundle extends InterruptPendingBundle {
  // VSSIP, VSTIP, VSEIP, localIP is writable
  this.getVS.foreach(_.setRW().withReset(0.U))
  this.getLocal.foreach(_.setRW().withReset(0.U))
  this.LCOFIP.setRO().withReset(0.U)
}

class HieBundle extends InterruptEnableBundle {
  // All bits in hie are RO, since all registers implemented in mie.
}

class HipBundle extends InterruptPendingBundle {
  this.VSSIP.setRW().withReset(0.U) // aliasRW of mip.VSSIP when mideleg=1.
  this.VSTIP.setRO().withReset(0.U) // aliasRO of mip.VSTIP when mideleg=1. (hvip.VSTIP |　PLIC.VSTIP)
  this.VSEIP.setRO().withReset(0.U) // aliasRO of mip.VSEIP when mideleg=1. (hvip.VSEIP | hgeip(hstatus.VGEIN) | PLIC.VSEIP)
  this.SGEIP.setRO().withReset(0.U) // aliasRO of mip.SGEIP (|(hgeip & hegie))
}

class HvienBundle extends InterruptEnableBundle {
  // Ref: riscv interrupt spec - 6.3.2 Virtual interrupts for VS level
  // Bits 12:0 of hvien are reserved and must be read-only zeros.
  // For interrupt numbers 13–63, implementations may freely choose which bits of hvien are writable
  // and which bits are read-only zero or one.
  this.getLocal.foreach(_.setRW().withReset(0.U))
  this.LCOFIE.setRO().withReset(0.U)
}

class HgeieBundle(implicit val p: Parameters) extends CSRBundle with HasSoCParameter {
  val ie = RW(soc.IMSICParams.geilen, 1).withReset(0.U)
  // bit 0 is read only 0
}

class HgeipBundle(implicit val p: Parameters) extends CSRBundle with HasSoCParameter {
  val ip = RO(soc.IMSICParams.geilen, 1)
  // bit 0 is read only 0
}

class HedelegBundle extends ExceptionBundle {
  this.getALL.foreach(_.setRW().withReset(0.U))
  // The default configs are RW
  this.EX_HSCALL.setRO().withReset(0.U)
  this.EX_VSCALL.setRO().withReset(0.U)
  this.EX_MCALL .setRO().withReset(0.U)
  this.EX_IGPF  .setRO().withReset(0.U)
  this.EX_LGPF  .setRO().withReset(0.U)
  this.EX_VI    .setRO().withReset(0.U)
  this.EX_SGPF  .setRO().withReset(0.U)
  this.EX_DBLTRP.setRO().withReset(0.U) // double trap is not delegatable
}

class HidelegBundle extends InterruptBundle {
  this.getALL.foreach(_.setRW().withReset(0.U))
  // default RW
  this.SSI .setRO().withReset(0.U)
  this.MSI .setRO().withReset(0.U)
  this.STI .setRO().withReset(0.U)
  this.MTI .setRO().withReset(0.U)
  this.SEI .setRO().withReset(0.U)
  this.MEI .setRO().withReset(0.U)
  this.SGEI.setRO().withReset(0.U)
  this.getLocal.foreach(_.setRO().withReset(0.U))
  this.LCOFI.setRW().withReset(0.U)
}

class HipToHvip extends Bundle {
  val VSSIP = ValidIO(RW(0))
}

class SipToHvip extends ToAliasIpLocalPart {

}

class HieToMie extends IeValidBundle {
  this.getVS.foreach(_.bits.setRW())
  this.SGEIE.bits.setRW()
}

class HvictlBundle extends CSRBundle {
  // Virtual Trap Interrupt control
  val VTI = RW(30).withReset(0.U)
  // WARL in AIA spec.
  // RW, since we support max width of IID
  val IID = RW(15 + HIIDWidth, 16).withReset(0.U)
  // determines the interrupt’s presumed default priority order relative to a (virtual) supervisor external interrupt (SEI), major identity 9
  // 0 = interrupt has higher default priority than an SEI
  // 1 = interrupt has lower default priority than an SEI
  // When hvictl.IID = 9, DPR is ignored.
  // Todo: sort the interrupt specified by hvictl with DPR
  val DPR = RW(9).withReset(0.U)
  val IPRIOM = RW(8).withReset(0.U)
  val IPRIO = RW(7, 0).withReset(0.U)
}

class Hviprio1Bundle extends CSRBundle {
  val PrioSSI = RW(15,  8).withReset(0.U)
  val PrioSTI = RW(31, 24).withReset(0.U)
  val PrioCOI = RW(47, 40).withReset(0.U)
  val Prio14  = RW(55, 48).withReset(0.U)
  val Prio15  = RW(63, 56).withReset(0.U)
}

class Hviprio2Bundle extends FieldInitBundle

class HgatpBundle extends CSRBundle {
  val MODE = HgatpMode(63, 60, wNoFilter).withReset(HgatpMode.Bare)
  // WARL in privileged spec.
  // RW, since we support max width of VMID
  val VMID = RW(44 - 1 + VMIDLEN, 44).withReset(0.U)
  val PPN = RW(43, 0).withReset(0.U)
}

class HEnvCfg extends EnvCfg {
  if (CSRConfig.EXT_SSTC) {
    this.STCE.setRW().withReset(1.U)
  }
  this.PBMTE.setRW().withReset(0.U)
  if (CSRConfig.EXT_DBLTRP) {
    // software write envcfg to open ssdbltrp if need
    // set 0 to pass ci
    this.DTE.setRW().withReset(0.U)
  }
}

class Htimedelta extends FieldInitBundle

trait HypervisorBundle { self: CSRModule[_] =>
  val hstatus = IO(Input(new HstatusBundle))
}

trait HasHypervisorEnvBundle { self: CSRModule[_] =>
  val menvcfg = IO(Input(new MEnvCfg))
}
