//MC{hide}
/***************************************************************************************
* Copyright (c) 2024 Beijing Institute of Open Source Chip (BOSC)
*
* ChiselAIA is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

package aia

import chisel3._
import chisel3.util._
import freechips.rocketchip.diplomacy._
import org.chipsalliance.cde.config.Parameters
import freechips.rocketchip.tilelink._
import freechips.rocketchip.devices.tilelink._
import freechips.rocketchip.amba.axi4._
import freechips.rocketchip.regmapper._
import utility._

case class APLICParams(
  //MC APLIC接收的中断源数量的对数。
  //MC 默认值7表示APLIC支持最多128（2^7）个中断源。
  //MC **注意**：`aplicIntSrcWidth`必须小于`imsicIntSrcWidth`，
  //MC 因为APLIC的中断源将被转换为MSI，
  //MC 而APLIC转换成的MSI是IMSIC中断源的子集。
  //MC （Logarithm of number of interrupt sources to APLIC:
  //MC The default 7 means APLIC support at most 128 (2^7) interrupt sources.
  //MC **Note**: `aplicIntSrcWidth` must be **less than** `imsicIntSrcWidth`,
  //MC as APLIC interrupt sources are converted to MSIs,
  //MC which are a subset of IMSIC's interrupt sources）：
  //MC{visible}
  aplicIntSrcWidth: Int = 7,
  imsicIntSrcWidth: Int = 9,
  //MC 👉 APLIC域的基地址（Base address of APLIC domains）:
  baseAddr: Long = 0x19960000L,
  //MC **注意**：下述中括号内的变量与AIA规范中的一致（第3.6节：用于多个中断文件的内存区域排列）。
  //MC
  //MC **Note**: The following variables in bracket align with the AIA specification (Section 3.6: Memory Region Arrangement for Multiple Interrupt Files).
  //MC
  //MC 👉 每个组的成员数量（Number of members per group）[\\(h_{max}\\)]：
  membersNum      : Int  = 2           ,
  //MC 👉 所有IMSIC的机器态中断文件的基地址（Base address of machine-level interrupt files for all IMSICs）[\\(A\\)]：
  mBaseAddr       : Long = 0x61000000L ,
  //MC 👉 所有IMSIC的监管态和客户态中断文件的基地址（Base addr for supervisor-level and guest-level interrupt files for all IMSICs）[\\(B\\)]:
  sgBaseAddr      : Long = 0x82900000L ,
  //MC 👉 组的数量（Number of groups ）[\\(g_{max}\\)]:
  groupsNum       : Int  = 1           ,
  //MC 👉 客户中断文件的数量（Number of guest interrupt files）:
  geilen          : Int  = 7           ,
  //MC{hide}
  //MC{hide}
) {
  require(aplicIntSrcWidth <= 10, f"aplicIntSrcWidth=${aplicIntSrcWidth}, must not greater than log2(1024)=10, as there are at most 1023 sourcecfgs")
  lazy val intSrcNum: Int = pow2(aplicIntSrcWidth).toInt
  lazy val ixNum: Int = pow2(aplicIntSrcWidth).toInt / 32
  lazy val domainMemWidth : Int  = 14 // interrupt file memory region width: 14-bit width => 16KB size

  lazy val intFileMemWidth : Int  = 12        // interrupt file memory region width: 12-bit width => 4KB size
  // require(mStrideWidth >= intFileMemWidth)
  lazy val mStrideWidth    : Int  = intFileMemWidth // C: stride between each machine-level interrupt files
  // require(sgStrideWidth >= log2Ceil(geilen+1) + intFileMemWidth)
  lazy val sgStrideWidth   : Int = log2Ceil(geilen+1) + intFileMemWidth // D: stride between each supervisor- and guest-level interrupt files
  // require(groupStrideWidth >= k + math.max(mStrideWidth, sgStrideWidth))
  lazy val membersWidth    : Int = log2Ceil(membersNum) // k
  require((mBaseAddr & (pow2(membersWidth + mStrideWidth) -1)) == 0, "mBaseAddr should be aligned to a 2^(k+C)")
  lazy val groupStrideWidth: Int = membersWidth + math.max(mStrideWidth, sgStrideWidth) // E: stride between each interrupt file groups
  lazy val groupsWidth     : Int = log2Ceil(groupsNum) // j
  require((sgBaseAddr & (pow2(membersWidth + sgStrideWidth) - 1)) == 0, "sgBaseAddr should be aligned to a 2^(k+D)")
  require(( ((pow2(groupsWidth)-1) * pow2(groupStrideWidth)) & mBaseAddr ) == 0)
  require(( ((pow2(groupsWidth)-1) * pow2(groupStrideWidth)) & sgBaseAddr) == 0)

  def hartIndex_to_gh(hartIndex: Int): (Int, Int) = {
    val g = (hartIndex>>membersWidth) & (pow2(groupsWidth)-1)
    val h = hartIndex & (pow2(membersWidth)-1)
    (g.toInt, h.toInt)
  }
  def gh_to_hartIndex(g: Int, h: Int): Int = {
    (g<<membersWidth) | h
  }
}

class APLIC(
  params: APLICParams,
  beatBytes: Int = 4,
)(implicit p: Parameters) extends Module {
  println(f"APLICParams.membersWidth:      ${params.membersWidth    }%d")
  println(f"APLICParams.groupsWidth:       ${params.groupsWidth     }%d")
  println(f"APLICParams.membersNum:        ${params.membersNum      }%d")
  println(f"APLICParams.mBaseAddr:       0x${params.mBaseAddr       }%x")
  println(f"APLICParams.mStrideWidth:      ${params.mStrideWidth    }%d")
  println(f"APLICParams.sgBaseAddr:      0x${params.sgBaseAddr      }%x")
  println(f"APLICParams.sgStrideWidth:     ${params.sgStrideWidth   }%d")
  println(f"APLICParams.geilen:            ${params.geilen          }%d")
  println(f"APLICParams.groupsNum:         ${params.groupsNum       }%d")
  println(f"APLICParams.groupStrideWidth:  ${params.groupStrideWidth}%d")

  class Domain(
    baseAddr: Long, // base address for this aplic domain
    imsicBaseAddr: Long, // base address for imsic's interrupt files
    imsicMemberStrideWidth: Int, // C, D: stride between each interrupt files
    imsicGeilen: Int, // number of guest interrupt files, it is 0 for machine-level domain
  )(implicit p: Parameters) extends Module {
    override val desiredName = "Domain"
    class MSIBundle extends Bundle {
      val addr = Output(UInt(64.W))
      val data = Output(UInt(32.W))
    }
    object MSIBundle { def apply(a: UInt, d: UInt) = {
      val m = Wire(new MSIBundle); m.addr:=a; m.data:=d; m }}
    val io = IO(new Bundle {
      val msi = Decoupled(new MSIBundle)
      val ack = Input(Bool())
      val (regmapIn, regmapOut) = {
        val regmapParams = RegMapperParams(params.domainMemWidth-log2Up(beatBytes), beatBytes)
        ( Flipped(Decoupled(new RegMapperInput(regmapParams))),
          Decoupled(new RegMapperOutput(regmapParams)) )
    }})
    val intSrcs = IO(Input(Vec(params.intSrcNum, Bool())))
    val intSrcsDelegated = IO(Output(Vec(params.intSrcNum, Bool())))

    private val domaincfg = new Bundle {
      val high = 0x80.U(8.W)
      val IE   = RegInit(false.B)
      val DM   = true.B // only support MSI delivery mode
      val BE   = false.B // only support little endian
      val r = high<<24 | IE<<8 | DM<<2 | BE
      def w(data:UInt):Unit = IE := data(8)
    }
    private val sourcecfgs = new Bundle {
      val List(inactive, detached, reserved2, reserved3, edge1, edge0, level1, level0) = Enum(8)
      class Sourcecfg extends Bundle {
        val D = Bool()
        // Currently only support one machine-level domain and one supervisor-level domain,
        // therefore, ChildIndex is not needed.
        val SM = UInt(3.W)
      }
      val regs = RegInit(VecInit.fill(params.intSrcNum)(0.U.asTypeOf(new Sourcecfg)))
      def rI(i:Int): UInt = regs(i).D<<10 | Mux(regs(i).D, 0.U, regs(i).SM)
      def wI(i:Int, data:UInt): Unit = {
        val D=data(10); val SM=data(2,0)
        regs(i).D := D
        regs(i).SM := Mux(D, 0.U, Mux(SM===reserved2||SM===reserved3, inactive, SM))
      }
      val actives = VecInit(regs.map(reg => ~reg.D && reg.SM=/=inactive))
    }
    abstract class IXs extends Bundle {
      protected val bits = RegInit(VecInit.fill(params.intSrcNum){false.B})
      val bits0 = VecInit(false.B +: bits.drop(1)) // bits0(0) is read-only 0
      def r32I(i:Int): UInt = Cat((0 until 32).map(j => rBitUI((i<<log2Ceil(32)|j).U)).reverse)
      def w32I(i:Int, d32:UInt): Unit = (0 until 32).map(j => wBitUI((i<<log2Ceil(32)|j).U, d32(j)))
      def rBitUI(ui:UInt): Bool = bits0(ui) & sourcecfgs.actives(ui)
      def wBitUI(ui:UInt, bit:Bool): Unit
    }
    private val ips = new IXs {
      def wBitUI(ui:UInt, bit:Bool): Unit = when (sourcecfgs.actives(ui)) {
        when (sourcecfgs.regs(ui).SM===sourcecfgs.level1 || sourcecfgs.regs(ui).SM===sourcecfgs.level0) {
          when (domaincfg.DM) {

          }.otherwise {/* Currently not support domaincfg.DM===0 */}
        }.otherwise { bits(ui):=bit }
      }
    }
    private val intSrcsRectified = Wire(Vec(params.intSrcNum, Bool()))
    private val ies = new IXs {
      def wBitUI(ui:UInt, bit:Bool): Unit = when (sourcecfgs.actives(ui)) {bits(ui):=bit}
    }
    private val genmsi = new Bundle {
      val HartIndex = RegInit(0.U(params.groupsWidth.W + params.membersWidth.W))
      val Busy      = RegInit(false.B)
      val EIID      = RegInit(0.U(params.imsicIntSrcWidth.W))
      def r = Mux(domaincfg.DM, HartIndex<<18 | Busy<<12 | EIID, 0.U)
      def w(data:UInt) = {
        when (domaincfg.DM && ~Busy) { HartIndex:=data(31,18); Busy:=true.B; EIID:=data(10,0); }
      }
    }
    private val targets = new Bundle {
      class Target extends Bundle {
        val HartIndex  = UInt(params.groupsWidth.W + params.membersWidth.W)
        val GuestIndex = UInt(if (imsicGeilen==0) 0.W else log2Ceil(imsicGeilen).W)
        val EIID       = UInt(params.imsicIntSrcWidth.W)
      }
      val regs = RegInit(VecInit.fill(params.intSrcNum){0.U.asTypeOf(new Target)})
      def rI(i:Int): UInt = Mux(sourcecfgs.actives(i),
        regs(i).HartIndex<<18 | regs(i).GuestIndex<<12 | regs(i).EIID, 0.U
      )
      def wI(i:Int, data:UInt): Unit = {
        when (sourcecfgs.actives(i)) {
          regs(i).HartIndex := data(31,18)
          regs(i).GuestIndex := data(17,12)
          regs(i).EIID := data(10,0)
      }}
    }

    // Writing ips priorities:
    // * 3st: regmapped regs: including setips, setipnum, in_clrips, clripnum
    // * 2nd: intSrcsTriggered, which sets the corresponding ip
    // * 1rd: send MSI, which cleans the corresponding ip
    // For more details about how ip-writing priority is achieved,
    // see FIRRTL Specification's chapter Conditional Last Connect Semantics.
    locally {
      val intSrcsRectified32 = Wire(Vec(pow2(params.aplicIntSrcWidth-5).toInt, UInt(32.W)))
      intSrcsRectified32.zipWithIndex.map { case (rect32:UInt, i:Int) => {
        rect32 := Cat(intSrcsRectified.slice(i*32, i*32+32).reverse)
      }}
      def RWF_setixs(i:Int, ixs:IXs) = RegWriteFn((valid, data) => {
        when(valid) {ixs.w32I(i, ixs.r32I(i) | data)}; true.B })
      def RWF_setipnum = RegWriteFn((valid, data) => {
        when (valid && data=/=0.U) { ips.wBitUI(data(params.aplicIntSrcWidth-1,0), true.B) }; true.B })
      def RWF_setclrixnum(setclr:Bool, ixs:IXs) = RegWriteFn((valid, data) => {
        when (valid && data=/=0.U) { ixs.wBitUI(data(params.aplicIntSrcWidth-1,0), setclr) }; true.B })
      def RWF_clrixs(i:Int, ixs:IXs) = RegWriteFn((valid, data) => {
        when (valid) { ixs.w32I(i, ixs.r32I(i) & ~data) }; true.B })
      io.regmapOut <> RegMapper(beatBytes, 1, true, io.regmapIn,
        /*domaincfg*/   0x0000 -> Seq(RegField(32, domaincfg.r, RegWriteFn((v, d)=>{ when(v){domaincfg.w(d)}; true.B }))),
        /*sourcecfgs*/  0x0004 -> (1 until params.intSrcNum).map(i => RegField(32, sourcecfgs.rI(i),
          RegWriteFn((v, d)=>{ when(v){sourcecfgs.wI(i, d)}; true.B }))),
        /*mmsiaddrcfgh*/0x1BC4 -> Seq(RegField(32, 0x80000000L.U, RegWriteFn(():Unit))), // hardwired *msiaddrcfg* regs
        /*setips*/      0x1C00 -> (0 until params.ixNum).map(i => RegField(32, ips.r32I(i), RWF_setixs(i, ips))),
        /*setipnum*/    0x1CDC -> Seq(RegField(32, 0.U, RWF_setipnum)),
        /*in_clrips*/   0x1D00 -> (0 until params.ixNum).map(i => RegField(32, intSrcsRectified32(i), RWF_clrixs(i, ips))),
        /*clripnum*/    0x1DDC -> Seq(RegField(32, 0.U, RWF_setclrixnum(false.B, ips))),
        /*seties*/      0x1E00 -> (0 until params.ixNum).map(i => RegField(32, ies.r32I(i), RWF_setixs(i, ies))),
        /*setienum*/    0x1EDC -> Seq(RegField(32, 0.U, RWF_setclrixnum(true.B, ies))),
        /*clries*/      0x1F00 -> (0 until params.ixNum).map(i => RegField(32, 0.U, RWF_clrixs(i, ies))),
        /*clrienum*/    0x1FDC -> Seq(RegField(32, 0.U, RWF_setclrixnum(false.B, ies))),
        /*setipnum_le*/ 0x2000 -> Seq(RegField(32, 0.U, RWF_setipnum)),
        /*setipnum_be*/ 0x2004 -> Seq(RegField(32, 0.U, RegWriteFn(():Unit))), // setipnum_be not implemented
        /*genmsi*/      0x3000 -> Seq(RegField(32, genmsi.r, RegWriteFn((v,d)=>{ when(v){genmsi.w(d)}; true.B }))),
        /*targets*/     0x3004 -> (1 until params.intSrcNum).map(i => RegField(32, targets.rI(i),
          RegWriteFn((v, d)=>{ when(v){targets.wI(i, d)}; true.B }))),
      )
    }

    private val intSrcsSynced = RegNextN(intSrcs, 3)
    intSrcsRectified(0) := false.B
    (1 until params.intSrcNum).map(i => {
      val (rect, sync, sm) = (intSrcsRectified(i), intSrcsSynced(i), sourcecfgs.regs(i).SM)
      when      (sm===sourcecfgs.edge1 || sm===sourcecfgs.level1) {
        rect := sync
      }.elsewhen(sm===sourcecfgs.edge0 || sm===sourcecfgs.level0) {
        rect := !sync
      }.otherwise {
        rect := false.B
      }
    })
    private val intSrcsTriggered = Wire(Vec(params.intSrcNum, Bool())); /*for debug*/dontTouch(intSrcsTriggered)
    (intSrcsTriggered zip intSrcsRectified).map { case (trigger, rect) => {
      trigger := rect && !RegNext(rect)
    }}
    intSrcsTriggered.zipWithIndex.map { case (trigger:Bool, i:Int) =>
      when(trigger) {ips.wBitUI(i.U, true.B)}
    }

    // The ":+ true.B" trick explain:
    //  Append true.B to handle the cornor case, where all bits in ip and ie are disabled.
    //  If do not append true.B, then we need to check whether the ip & ie are empty,
    //  otherwise, the returned topei will become the max index, that is 2^aplicIntSrcWidth-1
    //  [0,     2^aplicIntSrcWidth-1] :+ 2^aplicIntSrcWidth
    private val topi = Wire(UInt(params.aplicIntSrcWidth.W)); /*for debug*/dontTouch(topi)
    topi := ParallelPriorityMux((
      (ips.bits0:+true.B) zip (ies.bits0:+true.B)
    ).zipWithIndex.map {
      case ((p: Bool, e: Bool), i: Int) => (p & e, i.U)
    })
    // send MSI
    locally {
      val idle :: waiting_ack :: Nil = Enum(2)
      val state = RegInit(idle)

      def getMSIAddr(HartIndex:UInt, guestID:UInt): UInt = {
        val groupID = if (params.groupsWidth == 0) 0.U
          else HartIndex(params.groupsWidth+params.membersWidth-1, params.membersWidth)
        val memberID = if (params.membersWidth == 0) 0.U
          else HartIndex(params.membersWidth-1, 0)
        // It is recommended to hardwire *msiaddrcfg* by the manual:
        // "For any given system, these addresses are fixed and should be hardwired into the APLIC if possible."
        imsicBaseAddr.U |
          (groupID<<params.groupStrideWidth) |
          (memberID<<imsicMemberStrideWidth) |
          (guestID<<params.intFileMemWidth)
      }
      val genmsiBits = MSIBundle(getMSIAddr(genmsi.HartIndex, 0.U), genmsi.EIID)
      val target = targets.regs(topi)
      val topiBits = MSIBundle(getMSIAddr(target.HartIndex, target.GuestIndex), target.EIID)

      // A pending extempore MSI (genmsi) should be sent by the APLIC with minimal delay.
      io.msi.bits := Mux(genmsi.Busy, genmsiBits, topiBits)
      io.msi.valid := (state===idle) && (genmsi.Busy || (domaincfg.IE && topi=/=0.U))

      switch (state) {
        is (idle) { when (io.msi.fire) { state := waiting_ack } }
        is (waiting_ack) { when (io.ack) { state := idle
          when (genmsi.Busy) { genmsi.Busy := false.B }
          .otherwise { ips.wBitUI(topi, false.B) }
      }}}
    }

    // delegate
    intSrcsDelegated := (sourcecfgs.regs zip intSrcs).map {case (r, i:Bool) => r.D&i}
  }

  // domain(0) is m domain, domain(1) is sg domain
  private val domains = Seq(Module(new Domain(
    params.baseAddr,
    params.mBaseAddr,
    params.mStrideWidth,
    0,
  )), Module(new Domain(
    params.baseAddr + pow2(params.domainMemWidth),
    params.sgBaseAddr,
    params.sgStrideWidth,
    params.geilen,
  )))
  val ios = domains.map(d => { val io = IO(d.io.cloneType); io <> d.io; io })

  val intSrcs = IO(Input(Vec(params.intSrcNum, Bool())))
  domains(0).intSrcs := intSrcs
  domains(1).intSrcs := domains(0).intSrcsDelegated
}

class TLAPLIC(
  params: APLICParams,
  beatBytes: Int = 4,
)(implicit p: Parameters) extends LazyModule {
  val fromCPU = LazyModule(new TLXbar).node
  val toIMSIC = LazyModule(new TLXbar).node
  private val domainFromCPUs = Seq(
    params.baseAddr, params.baseAddr + pow2(params.domainMemWidth)
  ).map ( baseAddr => {
    val domainFromCPU = TLRegMapperNode(
      address = Seq(AddressSet(baseAddr, pow2(params.domainMemWidth)-1)),
      beatBytes = beatBytes)
    domainFromCPU := fromCPU; domainFromCPU
  })
  private val domainToIMSICs = (0 until 2).map (_ => {
    val domainToIMSIC = TLClientNode(Seq(TLMasterPortParameters.v1(Seq(TLMasterParameters.v1("toimsic", IdRange(0,16))))))
    toIMSIC := domainToIMSIC; domainToIMSIC
  })

  lazy val module = new Imp
  class Imp extends LazyModuleImp(this) {
    val intSrcs = IO(Input(Vec(params.intSrcNum, Bool())))
    private val aplic = Module(new APLIC(params, beatBytes))
    aplic.intSrcs := intSrcs

    (domainToIMSICs zip aplic.ios).map { case (domainToIMSIC, aplicIO) => {
      val (tl, edge) = domainToIMSIC.out.head
      val msi = aplicIO.msi
      val (_, bits) = edge.Put(0.U, msi.bits.addr, 2.U, msi.bits.data)
      tl.a.bits := bits
      tl.a.valid := msi.valid
      msi.ready := tl.a.ready
      tl.d.ready := true.B
      aplicIO.ack := tl.d.valid
    }}

    (domainFromCPUs zip aplic.ios).map { case (domainFromCPU, aplicIO) => {
      domainFromCPU.regmap(aplicIO.regmapIn, aplicIO.regmapOut)
    }}
  }
}

class AXI4APLIC(
  params: APLICParams,
  beatBytes: Int = 4,
)(implicit p: Parameters) extends LazyModule {
  val fromCPU = LazyModule(new AXI4Xbar).node
  val toIMSIC = LazyModule(new AXI4Xbar).node
  private val domainFromCPUs = Seq(
    params.baseAddr, params.baseAddr + pow2(params.domainMemWidth)
  ).map ( baseAddr => {
    val domainFromCPU = AXI4RegMapperNode(
      address = AddressSet(baseAddr, pow2(params.domainMemWidth)-1),
      beatBytes = beatBytes)
    domainFromCPU := fromCPU; domainFromCPU
  })
  private val domainToIMSICs = (0 until 2).map (_ => {
    val domainToIMSIC = AXI4MasterNode(Seq(AXI4MasterPortParameters(Seq(AXI4MasterParameters("toimsic", IdRange(0,16))))))
    toIMSIC := domainToIMSIC; domainToIMSIC
  })

  lazy val module = new Imp
  class Imp extends LazyModuleImp(this) {
    val intSrcs = IO(Input(Vec(params.intSrcNum, Bool())))
    private val aplic = Module(new APLIC(params, beatBytes))
    aplic.intSrcs := intSrcs

    (domainToIMSICs zip aplic.ios).map { case (domainToIMSIC, aplicIO) => {
      val (axi, edge_params) = domainToIMSIC.out.head
      val msi = aplicIO.msi
      axi.aw.bits.addr := msi.bits.addr
      axi.aw.bits.size := 2.U
      axi.aw.valid := msi.valid
      axi.w.bits.data := msi.bits.data
      axi.w.bits.strb := MaskGen(msi.bits.addr, 2.U, beatBytes)
      axi.w.bits.last := true.B
      axi.w.valid := msi.valid
      msi.ready := axi.aw.ready & axi.w.ready
      axi.b.ready := true.B
      aplicIO.ack := axi.b.valid
    }}

    (domainFromCPUs zip aplic.ios).map { case (domainFromCPU, aplicIO) => {
      domainFromCPU.regmap(aplicIO.regmapIn, aplicIO.regmapOut)
    }}
  }
}

when (intSrcsRectified(ui)) { bits(ui):=bit }


/***************************************************************************************
* Copyright (c) 2024 Beijing Institute of Open Source Chip (BOSC)
*
* ChiselAIA is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

package aia

import chisel3.{IO, _}
import chisel3.util._
import freechips.rocketchip.diplomacy._
import org.chipsalliance.cde.config.{Parameters, Config}
import freechips.rocketchip.amba.axi4._
// _root_ disambiguates from package chisel3.util.circt if user imports chisel3.util._
import _root_.circt.stage.ChiselStage

class AXI4AIA()(implicit p: Parameters) extends LazyModule {
  val toAIA = AXI4MasterNode(
    Seq(AXI4MasterPortParameters(
      Seq(AXI4MasterParameters("aia_axi4", IdRange(0, 16)))
    )))
  val toAIA_xbar = LazyModule(new AXI4Xbar).node
  toAIA_xbar := toAIA

  // Here we create 2 imsic groups, each group contains two 2 CPUs
  val imsic_params = IMSICParams(EnableImsicAsyncBridge = true)
  val aplic_params = APLICParams(groupsNum = 2, membersNum = 2)
  val imsics_fromMem_xbar = LazyModule(new AXI4Xbar).node
  imsics_fromMem_xbar := toAIA_xbar

  val imsics = (0 until 4).map(i => {
    val (groupID, memberID) = aplic_params.hartIndex_to_gh(i)
    require(groupID < aplic_params.groupsNum, f"groupID ${groupID} should less than groupsNum ${aplic_params.groupsNum}")
    require(memberID < aplic_params.membersNum, f"memberID ${memberID} should less than membersNum ${aplic_params.membersNum}")
    println(f"Generating IMSIC groupID=0x${groupID}%x memberID=0x${memberID}%x")
    val map = LazyModule(new AXI4Map(addrSet => addrSet.base.toLong match {
      case imsic_params.mAddr => groupID * pow2(aplic_params.groupStrideWidth) + aplic_params.mBaseAddr + memberID * pow2(aplic_params.mStrideWidth)
      case imsic_params.sgAddr => groupID * pow2(aplic_params.groupStrideWidth) + aplic_params.sgBaseAddr + memberID * pow2(aplic_params.sgStrideWidth)
      case imsic_params.tee_mAddr => groupID * pow2(aplic_params.groupStrideWidth) + 0x40010000L + memberID * pow2(aplic_params.mStrideWidth)
      case imsic_params.tee_sgAddr => groupID * pow2(aplic_params.groupStrideWidth) + 0x80010000L + memberID * pow2(aplic_params.sgStrideWidth)

    })(Parameters.empty)).node

    val teemap = LazyModule(new AXI4Map(addrSet => addrSet.base.toLong match {
      case imsic_params.mAddr => groupID * pow2(aplic_params.groupStrideWidth) + 0x40010000L + memberID * pow2(aplic_params.mStrideWidth)
      case imsic_params.sgAddr => groupID * pow2(aplic_params.groupStrideWidth) + 0x80010000L + memberID * pow2(aplic_params.sgStrideWidth)
<TARGET>
    })(Parameters.empty)).node

    val imsic = LazyModule(new AXI4IMSIC(imsic_params)(new Config((site, here, up) => {
      case IMSICParameKey => IMSICParameters(HasTEEIMSIC = false)
    })))

//    imsic.axireg.axireg.axi4tolite.head.node := map := imsics_fromMem_xbar
    imsic.axireg.imsic_xbar1to2 := map := imsics_fromMem_xbar
//    imsic.axireg.tee_axireg.foreach { tee_axireg => tee_axireg.axi4tolite.head.node := teemap := imsics_fromMem_xbar }
    imsic
  })

  val aplic = LazyModule(new AXI4APLIC(aplic_params)(Parameters.empty))
  aplic.fromCPU := toAIA_xbar
  imsics_fromMem_xbar := aplic.toIMSIC

  lazy val module = new LazyModuleImp(this) with HasIMSICParameters{
    toAIA.makeIOs()(ValName("toaia"))
    (0 until 4).map(i => {
      val toCSR = IO(Output(chiselTypeOf(imsics(i).module.toCSR))).suggestName(f"toCSR${i}")
      val fromCSR = IO(Input(chiselTypeOf(imsics(i).module.fromCSR))).suggestName(f"fromCSR${i}")
      toCSR <> imsics(i).module.toCSR
      fromCSR <> imsics(i).module.fromCSR
    })
    val intSrcs = IO(Input(chiselTypeOf(aplic.module.intSrcs)))
    intSrcs <> aplic.module.intSrcs
    val sec_cmode = if (GHasTEEIMSIC) Some(IO(Input(Bool()))) else None
    val sec_notice_pending = if (GHasTEEIMSIC) Some(IO(Output(Vec(4,Bool())))) else None
    for (i <- 0 until 4) {
      imsics(i).module.soc_clock := clock
      imsics(i).module.soc_reset := reset
      sec_cmode.foreach { sec_cmode =>
        imsics(i).module.io_sec.foreach {
          instance_iosec =>
            instance_iosec.cmode := sec_cmode
        }
      }

      sec_notice_pending.foreach { sec_notice_pending =>
        imsics(i).module.io_sec.foreach {
          instance_iosec =>
            sec_notice_pending(i) := instance_iosec.notice_pending
        }
      }
    }
  }
}

/**
 * Generate Verilog sources
 */
object AXI4AIA extends App {

  val axi4top = LazyModule(new AXI4AIA()(
    Parameters.empty.alterPartial({
      case IMSICParameKey => IMSICParameters(HasTEEIMSIC=false)
    })
  ))

  ChiselStage.emitSystemVerilog(
    axi4top.module,
    args = Array("--dump-fir"),
    // more opts see: $CHISEL_FIRTOOL_PATH/firtool -h
    firtoolOpts = Array(
      "-disable-all-randomization",
      "-strip-debug-info",
      // without this, firtool will exit with error: Unhandled annotation
      "--disable-annotation-unknown",
      "--lowering-options=explicitBitcast,disallowLocalVariables,disallowPortDeclSharing,locationInfoStyle=none",
      "--split-verilog", "-o=gen_axi",
    )
  )
}

case _ => assert(false, f"unknown address ${addrSet.base}"); 0


/***************************************************************************************
* Copyright (c) 2024 Beijing Institute of Open Source Chip (BOSC)
*
* ChiselAIA is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

package aia

import chisel3._
import chisel3.util._
import freechips.rocketchip.diplomacy._
import org.chipsalliance.cde.config.{Parameters, Config}
import freechips.rocketchip.tilelink._
// _root_ disambiguates from package chisel3.util.circt if user imports chisel3.util._
import _root_.circt.stage.ChiselStage

class TLAIA()(implicit p: Parameters) extends LazyModule {
  val toAIA = TLClientNode(
    Seq(TLMasterPortParameters.v1(
      Seq(TLMasterParameters.v1("aia_tl", IdRange(0, 16)))
  )))
  val toAIA_xbar = LazyModule(new TLXbar).node
  toAIA_xbar := toAIA

  // Here we create 2 imsic groups, each group contains two 2 CPUs
  val imsic_params = IMSICParams(EnableImsicAsyncBridge = false)
  val aplic_params = APLICParams(groupsNum=2, membersNum=2)
  val imsics_fromMem_xbar = LazyModule(new TLXbar).node
  imsics_fromMem_xbar := toAIA_xbar

  val imsics = (0 until 4).map( i => {
    val (groupID,memberID) = aplic_params.hartIndex_to_gh(i)
    require(groupID < aplic_params.groupsNum,    f"groupID ${groupID} should less than groupsNum ${aplic_params.groupsNum}")

    println(f"Generating IMSIC groupID=0x${groupID }%x memberID=0x${memberID}%x")
    val map = LazyModule(new TLMap( addrSet => addrSet.base.toLong match {
      case imsic_params.mAddr => groupID * pow2(aplic_params.groupStrideWidth) + aplic_params.mBaseAddr + memberID * pow2(aplic_params.mStrideWidth)
      case imsic_params.sgAddr=> groupID * pow2(aplic_params.groupStrideWidth) + aplic_params.sgBaseAddr+ memberID * pow2(aplic_params.sgStrideWidth)
      case imsic_params.tee_mAddr => groupID * pow2(aplic_params.groupStrideWidth) + 0x40010000L + memberID * pow2(aplic_params.mStrideWidth)
      case imsic_params.tee_sgAddr => groupID * pow2(aplic_params.groupStrideWidth) + 0x80010000L + memberID * pow2(aplic_params.sgStrideWidth)
      case _ => assert(false, f"unknown address ${addrSet.base}"); 0
    })(Parameters.empty)).node

    val teemap = LazyModule(new TLMap(addrSet => addrSet.base.toLong match {
      case imsic_params.mAddr => groupID * pow2(aplic_params.groupStrideWidth) + 0x40010000L + memberID * pow2(aplic_params.mStrideWidth)
      case imsic_params.sgAddr => groupID * pow2(aplic_params.groupStrideWidth) + 0x80010000L + memberID * pow2(aplic_params.sgStrideWidth)
      case _ => assert(false, f"unknown address ${addrSet.base}"); 0
    })(Parameters.empty)).node
    val imsic = LazyModule(new TLIMSIC(imsic_params)(new Config((site, here, up) => {
      case IMSICParameKey => IMSICParameters(HasTEEIMSIC = false)
    })))

//    imsic.axireg.axireg.fromMem.head := map := imsics_fromMem_xbar
//    imsic.axireg.tee_axireg.foreach { tee_axireg => tee_axireg.fromMem.head := teemap := imsics_fromMem_xbar }
    imsic.axireg.imsic_xbar1to2 := map := imsics_fromMem_xbar
    imsic
  })

  val aplic = LazyModule(new TLAPLIC(aplic_params)(Parameters.empty))
  aplic.fromCPU := toAIA_xbar
  imsics_fromMem_xbar := aplic.toIMSIC

  lazy val module = new LazyModuleImp(this) with HasIMSICParameters{
    toAIA.makeIOs()(ValName("toaia"))
    (0 until 4).map (i => {
      val toCSR = IO(Output(chiselTypeOf(imsics(i).module.toCSR))).suggestName(f"toCSR${i}")
      val fromCSR = IO(Input(chiselTypeOf(imsics(i).module.fromCSR))).suggestName(f"fromCSR${i}")
      toCSR   <> imsics(i).module.toCSR
      fromCSR <> imsics(i).module.fromCSR
    })
    val intSrcs = IO(Input(chiselTypeOf(aplic.module.intSrcs)))
    intSrcs <> aplic.module.intSrcs
    val sec_cmode = if (GHasTEEIMSIC) Some(IO(Input(Bool()))) else None
    val sec_notice_pending = if (GHasTEEIMSIC) Some(IO(Output(Vec(4,Bool())))) else None
    for (i <- 0 until 4) {
      imsics(i).module.soc_clock := clock
      imsics(i).module.soc_reset := reset
      sec_cmode.foreach { sec_cmode =>
        imsics(i).module.io_sec.foreach {
          instance_iosec =>
            instance_iosec.cmode := sec_cmode
        }
      }
      sec_notice_pending.foreach { sec_notice_pending =>
        imsics(i).module.io_sec.foreach {
          instance_iosec =>
            sec_notice_pending(i) := instance_iosec.notice_pending
        }
      }
    }
  }
}

/**
 * Generate Verilog sources
 */
object TLAIA extends App {
  val top = LazyModule(new TLAIA()(
    Parameters.empty.alterPartial({
      case IMSICParameKey => IMSICParameters(HasTEEIMSIC=false)
    })
  ))

  ChiselStage.emitSystemVerilog(
    top.module,
    args = Array("--dump-fir"),
    // more opts see: $CHISEL_FIRTOOL_PATH/firtool -h
    firtoolOpts = Array(
      "-disable-all-randomization",
      "-strip-debug-info",
      // without this, firtool will exit with error: Unhandled annotation
      "--disable-annotation-unknown",
      "--lowering-options=explicitBitcast,disallowLocalVariables,disallowPortDeclSharing,locationInfoStyle=none",
      "--split-verilog", "-o=gen",
    )
  )
}

require(memberID < aplic_params.membersNum,  f"memberID ${memberID} should less than membersNum ${aplic_params.membersNum}")


package aia

import chisel3._
import chisel3.IO
import chisel3.util._
import freechips.rocketchip.amba.axi4._
import freechips.rocketchip.amba.axi4.AXI4Xbar
import freechips.rocketchip.devices.tilelink._
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.prci.ClockSinkDomain
import freechips.rocketchip.regmapper._
import freechips.rocketchip.tilelink._
import freechips.rocketchip.util._
import org.chipsalliance.cde.config.Parameters
import utility._

object RegMapDV {
  def Unwritable = null
  def apply(addr: Int, reg: UInt, wfn: UInt => UInt = (x => x)) = (addr, (reg, wfn))
  def generate(
      default: UInt,
      mapping: Map[Int, (UInt, UInt => UInt)],
      raddr:   UInt,
      rvld:    Bool,
      rdata:   UInt,
      rvalid:  Bool,
      waddr:   UInt,
      wen:     Bool,
      wdata:   UInt,
      wmask:   UInt,
      illegal_priv: Bool,
      illegal_op:   Bool
  ): Unit = {
    val chiselMapping = mapping.map { case (a, (r, w)) => (a.U, r, w) }
    when(rvld) {
      rdata := LookupTreeDefault(
        raddr,
        Cat(default),
        chiselMapping.map { case (a, r, _) => (a, r) }
      )
      rvalid := true.B
    }.otherwise {
      rdata  := 0.U((rdata.getWidth).W)
      rvalid := illegal_priv | illegal_op
    }

    chiselMapping.foreach { case (a, r, w) =>
      if (w != null) {
        when(wen && waddr === a && !illegal_priv && !illegal_op) {
          r := w(MaskData(r, wdata, wmask))
        }
      }
    }
  }
  def generate(
      default: UInt,
      mapping: Map[Int, (UInt, UInt => UInt)],
      addr:    UInt,
      rvld:    Bool,
      rdata:   UInt,
      rvalid:  Bool,
      wen:     Bool,
      wdata:   UInt,
      wmask:   UInt,
      illegal_priv: Bool,
      illegal_wdata_op:   Bool
  ): Unit = generate(default, mapping, addr, rvld, rdata, rvalid, addr, wen, wdata, wmask, illegal_priv, illegal_wdata_op)
}

// Based on Xiangshan NewCSR
object OpType extends ChiselEnum {
  val ILLEGAL = Value(0.U)
  val CSRRW   = Value(1.U)
  val CSRRS   = Value(2.U)
  val CSRRC   = Value(3.U)
}
object PrivType extends ChiselEnum {
  val U = Value(0.U)
  val S = Value(1.U)
  val H = Value(2.U)
  val M = Value(3.U)
}
class MSITransBundle(params: IMSICParams) extends Bundle {
  val vld_req = Input(Bool()) // request from axireg
  val data = Input(UInt(params.MSI_INFO_WIDTH.W))
  val vld_ack = Output(Bool())  // ack for axireg from imsic. which indicates imsic can work actively.
}
class ForCVMBundle extends Bundle {
  val cmode = Input(Bool()) // add port: cpu mode is tee or ree
  val notice_pending =
    Output(Bool()) // add port: interrupt pending of ree when cmode is tee,else interrupt pending of tee.
}
class AddrBundle(params: IMSICParams) extends  Bundle {
  val valid = Bool()                      // 表示 addr 是否有效
  val bits  = new Bundle {
    val addr = UInt(params.iselectWidth.W) // 存储实际地址值
    val virt  = Bool()
    val priv  = PrivType()
  }
}
class CSRToIMSICBundle(params: IMSICParams) extends Bundle {
  val addr  = new AddrBundle(params)
  val vgein = UInt(params.vgeinWidth.W)
  val wdata = ValidIO(new Bundle {
    val op   = OpType()
    val data = UInt(params.xlen.W)
  })
  val claims = Vec(params.privNum, Bool())
}
class IMSICToCSRBundle(params: IMSICParams) extends Bundle {
  val rdata    = ValidIO(UInt(params.xlen.W))
  val illegal  = Bool()
  val pendings = UInt(params.intFilesNum.W)
  val topeis   = Vec(params.privNum, UInt(32.W))
}
case class IMSICParams(
    // MC IMSIC中断源数量的对数，默认值8表示IMSIC支持最多512（2^9）个中断源

    // MC （Logarithm of number of interrupt sources to IMSIC.
    // MC The default 9 means IMSIC support at most 256 (2^9) interrupt sources）:
    // MC{visible}
    imsicIntSrcWidth: Int = 9,
    // MC 👉 本IMSIC的机器态中断文件的地址（Address of machine-level interrupt files for this IMSIC）：
    mAddr: Long = 0x00000L,
    // MC 👉 本IMSIC的监管态和客户态中断文件的地址（Addr for supervisor-level and guest-level interrupt files for this IMSIC）:
    sgAddr: Long = 0x10000L,
    // MC 👉 客户中断文件的数量（Number of guest interrupt files）:
    geilen: Int = 7,
    // MC vgein信号的位宽（The width of the vgein signal）:
    vgeinWidth: Int = 6,
    // MC iselect信号的位宽(The width of iselect signal):
    iselectWidth:           Int = 12,
    EnableImsicAsyncBridge: Boolean = true,
    HasTEEIMSIC:            Boolean = false,
    HartIDBits: Int = 9
    // MC{hide}
) {
  lazy val xlen: Int = 64 // currently only support xlen = 64
  lazy val xlenWidth = log2Ceil(xlen)
  require(
    imsicIntSrcWidth <= 11 && imsicIntSrcWidth >= 6,
    f"imsicIntSrcWidth=${imsicIntSrcWidth}, must not greater than log2(2048)=11, as there are at most 2048 eip/eie bits" +
      "must not be less than log2(64)=6, as there must be at least 64 eip/eie bits"
  )
  lazy val privNum:     Int = 3          // number of privilege modes: machine, supervisor, virtualized supervisor
  lazy val intFilesNum: Int = 2 + geilen // number of interrupt files, m, s, vs0, vs1, ...

  lazy val eixNum: Int = pow2(imsicIntSrcWidth).toInt / xlen // number of eip/eie registers
  lazy val intFileMemWidth: Int = 12 // interrupt file memory region width: 12-bit width => 4KB size
  lazy val tee_mshift: Int = HartIDBits + intFileMemWidth // 9: max 512 hart, bit10 is 1, tee imsic accessed.
  lazy val tee_sshift: Int = HartIDBits + log2Ceil(1+ geilen) + intFileMemWidth // 9: max 512 hart, bit10 is 1, tee imsic accessed.
  lazy val tee_mAddr: Long = mAddr + (1L << tee_mshift)
  lazy val tee_sgAddr: Long = sgAddr + (1L << tee_sshift)
  require(vgeinWidth >= log2Ceil(geilen))
  require(
    iselectWidth >= 8,
    f"iselectWidth=${iselectWidth} needs to be able to cover addr [0x70, 0xFF], that is from CSR eidelivery to CSR eie63"
  )
  lazy val INTP_FILE_WIDTH = log2Ceil(intFilesNum)
  lazy val MSI_INFO_WIDTH  = imsicIntSrcWidth + INTP_FILE_WIDTH
}

class IMSIC(
    params:    IMSICParams,
    beatBytes: Int = 4
)(implicit p: Parameters) extends Module {
  println(f"IMSICParams.geilen:            ${params.geilen}%d")

  class IMSICGateWay extends Module {
    // === io port define ===
    val msiio = IO(new MSITransBundle(params))
    val msi_data_o  = IO(Output(UInt(params.imsicIntSrcWidth.W)))
    val msi_valid_o = IO(Output(UInt(params.intFilesNum.W)))

    // === main body ===
    val msi_in = Wire(UInt(params.MSI_INFO_WIDTH.W))
    msi_in := msiio.data
    val msi_vld_req_cpu = WireInit(false.B)
    when(params.EnableImsicAsyncBridge.B) {
      msi_vld_req_cpu := AsyncResetSynchronizerShiftReg(msiio.vld_req, 3, 0)
    }.otherwise {
      msi_vld_req_cpu := msiio.vld_req
    }
    val msi_vld_ack_cpu = RegInit(false.B)
    when(msi_vld_req_cpu)(
      msi_vld_ack_cpu := true.B
    ).otherwise(
      msi_vld_ack_cpu := false.B
    )
    // generate the msi_vld_ack,to handle with the input msi request.
    msiio.vld_ack := msi_vld_ack_cpu
    val msi_vld_ris_cpu = msi_vld_req_cpu & (~msi_vld_ack_cpu) // rising of msi_vld_req
    val msi_data_catch  = RegInit(0.U(params.imsicIntSrcWidth.W))
    val msi_intf_valids = RegInit(0.U(params.intFilesNum.W))
    msi_data_o  := msi_data_catch(params.imsicIntSrcWidth - 1, 0)
    msi_valid_o := msi_intf_valids // multi-bis switch vector
    when(msi_vld_ris_cpu) {
      msi_data_catch := msi_in(params.imsicIntSrcWidth - 1, 0)
      msi_intf_valids := 1.U << msi_in(params.MSI_INFO_WIDTH - 1,params.imsicIntSrcWidth)
    }.otherwise {
      msi_intf_valids := 0.U
    }
  }
  class IntFile extends Module {
    override def desiredName = "IntFile"
    val fromCSR = IO(Input(new Bundle {
      val seteipnum = ValidIO(UInt(params.imsicIntSrcWidth.W))
      val addr      = ValidIO(UInt(params.iselectWidth.W))
      val virt      = Bool()
      val priv      = PrivType()
      val vgein     = UInt(params.vgeinWidth.W)
      val wdata = ValidIO(new Bundle {
        val op   = OpType()
        val data = UInt(params.xlen.W)
      })
      val claim = Bool()
    }))
    val toCSR = IO(Output(new Bundle {
      val rdata   = ValidIO(UInt(params.xlen.W))
      val illegal = Bool()
      val pending = Bool()
      val topei   = UInt(params.imsicIntSrcWidth.W)
    }))
    val illegal_io = IO(new Bundle {
      val illegal_priv = Input(Bool())
    })
    val illegal_priv = illegal_io.illegal_priv

    /// indirect CSRs
    val eidelivery  = RegInit(0.U(params.xlen.W))
    val eithreshold = RegInit(0.U(params.xlen.W))
    val eips        = RegInit(VecInit.fill(params.eixNum)(0.U(params.xlen.W)))
    val eies        = RegInit(VecInit.fill(params.eixNum)(0.U(params.xlen.W)))

    val illegal_wdata_op = WireDefault(false.B)
    locally { // scope for xiselect CSR reg map
      val wdata = WireDefault(0.U(params.xlen.W))
      val wmask = WireDefault(0.U(params.xlen.W))
      when(fromCSR.wdata.valid) {
        switch(fromCSR.wdata.bits.op) {
          // is(OpType.ILLEGAL) {
          //   illegal_wdata_op := true.B
          // }
          is(OpType.CSRRW) {
            wdata := fromCSR.wdata.bits.data
            wmask := Fill(params.xlen, 1.U)
          }
          is(OpType.CSRRS) {
            wdata := Fill(params.xlen, 1.U)
            wmask := fromCSR.wdata.bits.data
          }
          is(OpType.CSRRC) {
            wdata := 0.U
            wmask := fromCSR.wdata.bits.data
          }
        }
      }
      def bit0ReadOnlyZero(x: UInt): UInt = x & ~1.U(x.getWidth.W)
      def fixEIDelivery(x: UInt): UInt = x & 1.U
      RegMapDV.generate(
        0.U,
        Map(
          RegMapDV(0x70, eidelivery, fixEIDelivery),
          RegMapDV(0x72, eithreshold),
          RegMapDV(0x80, eips(0), bit0ReadOnlyZero),
          RegMapDV(0xc0, eies(0), bit0ReadOnlyZero)
        ) ++ eips.drop(1).zipWithIndex.map { case (eip: UInt, i: Int) =>
          RegMapDV(0x82 + i * 2, eip)
        } ++ eies.drop(1).zipWithIndex.map { case (eie: UInt, i: Int) =>
          RegMapDV(0xc2 + i * 2, eie)
        },
        /*raddr*/  fromCSR.addr.bits,
        /*rvld */  fromCSR.addr.valid,
        /*rdata*/  toCSR.rdata.bits,
        /*rvalid*/ toCSR.rdata.valid,
        /*waddr*/  fromCSR.addr.bits,
        /*wen  */  fromCSR.wdata.valid,
        /*wdata*/  wdata,
        /*wmask*/  wmask,
        /*priv*/   illegal_priv,
        /*op*/     illegal_wdata_op
      )
      val illegal_csr = WireDefault(false.B)
      when(fromCSR.addr.bits >= 0x80.U && fromCSR.addr.bits <= 0xFF.U &&
        fromCSR.addr.bits(0) === 1.U) {
          illegal_csr := true.B
      }
      toCSR.illegal := illegal_csr
    }
    locally {
      val index  = fromCSR.seteipnum.bits(params.imsicIntSrcWidth - 1, params.xlenWidth)
      val offset = fromCSR.seteipnum.bits(params.xlenWidth - 1, 0)
      when(fromCSR.seteipnum.valid) {
        // set eips bit
        eips(index) := eips(index) | UIntToOH(offset)
      }
    }

    locally { // scope for xtopei
      // The ":+ true.B" trick explain:
      //  Append true.B to handle the cornor case, where all bits in eip and eie are disabled.
      //  If do not append true.B, then we need to check whether the eip & eie are empty,
      //  otherwise, the returned topei will become the max index, that is 2^intSrcWidth-1
      // Noted: the support max interrupt sources number = 2^intSrcWidth
      //              [0,     2^intSrcWidth-1] :+ 2^intSrcWidth
      val eipBools = Cat(eips.reverse).asBools :+ true.B
      val eieBools = Cat(eies.reverse).asBools :+ true.B

      def xtopei_filter(xeidelivery: UInt, xeithreshold: UInt, xtopei: UInt): UInt = {
        val tmp_xtopei = Mux(xeidelivery(params.xlen - 1, 1) === 0.U, Mux(xeidelivery(0), xtopei, 0.U) , 0.U)
        // {
        //   all interrupts are enabled, when eithreshold == 1;
        //   interrupts, when i < eithreshold, are enabled;
        // } <=> interrupts, when i <= (eithreshold -1), are enabled
        Mux(tmp_xtopei <= (xeithreshold - 1.U), tmp_xtopei, 0.U)
      }
      toCSR.topei := xtopei_filter(
        eidelivery,
        eithreshold,
        ParallelPriorityMux(
          (eipBools zip eieBools).zipWithIndex.map {
            case ((p: Bool, e: Bool), i: Int) => (p & e, i.U)
          }
        )
      )
    } // end of scope for xtopei
    toCSR.pending := toCSR.topei =/= 0.U

    when(fromCSR.claim) {
      val index  = toCSR.topei(params.imsicIntSrcWidth - 1, params.xlenWidth)
      val offset = toCSR.topei(params.xlenWidth - 1, 0)
      // clear the pending bit indexed by xtopei in xeip
      eips(index) := eips(index) & ~UIntToOH(offset)
    }
  }
  val toCSR   = IO(Output(new IMSICToCSRBundle(params)))
  val fromCSR = IO(Input(new CSRToIMSICBundle(params)))
  val msiio   = IO(new MSITransBundle(params))
  val illegal_priv = WireInit(false.B)

  private val intFilesSelOH_r = WireDefault(0.U(params.intFilesNum.W))
  private val intFilesSelOH_w = WireDefault(0.U(params.intFilesNum.W))
  locally {
    when (fromCSR.addr.valid)
    {
      when(fromCSR.addr.bits.virt === false.B )
      {
        when(((fromCSR.addr.bits.priv.asUInt === 3.U) || (fromCSR.addr.bits.priv.asUInt === 1.U))/* && fromCSR.vgein === 0.U*/){
          illegal_priv := false.B
        }.otherwise{
          illegal_priv := true.B
        }
      }.otherwise{
        when(fromCSR.addr.bits.priv.asUInt === 1.U && (fromCSR.vgein >= 1.U) && (fromCSR.vgein < (params.geilen + 1).U((params.vgeinWidth+1).W)))
        {
          illegal_priv := false.B
        }.otherwise{
          illegal_priv := true.B
        }
      }
    }
    when (fromCSR.addr.valid && !illegal_priv) // read
    {
      val pv = Cat(fromCSR.addr.bits.priv.asUInt, fromCSR.addr.bits.virt)
      when(pv === Cat(PrivType.M.asUInt, false.B)){intFilesSelOH_r := UIntToOH(0.U)}
        .elsewhen(pv === Cat(PrivType.S.asUInt, false.B)){intFilesSelOH_r := UIntToOH(1.U)}
        .elsewhen(pv === Cat(PrivType.S.asUInt, true.B)){intFilesSelOH_r := UIntToOH(1.U((fromCSR.vgein.getWidth+1).W)
         + fromCSR.vgein.pad(params.vgeinWidth+1))
      }
    }
    when (fromCSR.addr.valid && fromCSR.wdata.valid && !(fromCSR.wdata.bits.op.asUInt === 0.U) && !illegal_priv) // write
    {
        val pv = Cat(fromCSR.addr.bits.priv.asUInt, fromCSR.addr.bits.virt)
        when(pv === Cat(PrivType.M.asUInt, false.B)){intFilesSelOH_w := UIntToOH(0.U)}
        .elsewhen(pv === Cat(PrivType.S.asUInt, false.B)){intFilesSelOH_w := UIntToOH(1.U)}
        .elsewhen(pv === Cat(PrivType.S.asUInt, true.B)){intFilesSelOH_w := UIntToOH(1.U((fromCSR.vgein.getWidth+1).W)
         + fromCSR.vgein.pad(params.vgeinWidth+1))
        }
    }
  }

  private val topeis_forEachIntFiles   = Wire(Vec(params.intFilesNum, UInt(params.imsicIntSrcWidth.W)))
  private val illegals_forEachIntFiles = Wire(Vec(params.intFilesNum, Bool()))
  // instance and connect IMSICGateWay.
  val imsicGateWay = Module(new IMSICGateWay)
  imsicGateWay.msiio <> msiio
  val pendings = Wire(Vec(params.intFilesNum,Bool()))
  val vec_rdata = Wire(Vec(params.intFilesNum, ValidIO(UInt(params.xlen.W))))
  Seq(1, 1 + params.geilen).zipWithIndex.map {
    case (intFilesNum: Int, i: Int) => {
      // j: index for S intFile: S, G1, G2, ...
      val maps = (0 until intFilesNum).map { j =>
        val flati = i + j
        val pi    = if (flati > 2) 2 else flati // index for privileges: M, S, VS.

        def sel_addr(old: AddrBundle): AddrBundle = {
          val new_ = Wire(new AddrBundle(params))
          new_.valid := old.valid & intFilesSelOH_r(flati)
          new_.bits.addr := old.bits.addr
          new_.bits.virt := old.bits.virt
          new_.bits.priv := old.bits.priv
          new_
        }
        def sel_wdata[T <: Data](old: Valid[T]): Valid[T] = {
          val new_ = Wire(Valid(chiselTypeOf(old.bits)))
          new_.bits  := old.bits
          new_.valid := old.valid & intFilesSelOH_w(flati)
          new_
        }

        val intFile = Module(new IntFile)
        // Preventing overflow
        when (flati.U((params.vgeinWidth + 1).W) === fromCSR.vgein.pad(params.vgeinWidth + 1)+1.U) {
          intFile.fromCSR.vgein := fromCSR.vgein
        } .otherwise {
          intFile.fromCSR.vgein := 0.U
        }
        val intfile_rdata_d = RegNext(intFile.toCSR.rdata)
        val msi_valid_delayed = RegNext(imsicGateWay.msi_valid_o(flati), false.B)
        intFile.fromCSR.seteipnum.bits  := imsicGateWay.msi_data_o
        intFile.fromCSR.seteipnum.valid := imsicGateWay.msi_valid_o(flati) | msi_valid_delayed
        intFile.fromCSR.addr.valid      := sel_addr(fromCSR.addr).valid
        intFile.fromCSR.addr.bits       := sel_addr(fromCSR.addr).bits.addr
        intFile.fromCSR.virt            := sel_addr(fromCSR.addr).bits.virt
        intFile.fromCSR.priv            := sel_addr(fromCSR.addr).bits.priv
        intFile.fromCSR.wdata           := sel_wdata(fromCSR.wdata)
        intFile.fromCSR.claim           := fromCSR.claims(pi)
        intFile.illegal_io.illegal_priv := illegal_priv
        vec_rdata(flati)                := intfile_rdata_d
        pendings(flati)                 := intFile.toCSR.pending
        topeis_forEachIntFiles(flati)   := intFile.toCSR.topei
        illegals_forEachIntFiles(flati) := intFile.toCSR.illegal
      }
    }
  }
  toCSR.rdata.valid   := vec_rdata.map(_.valid).reduce(_|_)
  toCSR.rdata.bits    := vec_rdata.map(_.bits).reduce(_|_)
  toCSR.pendings := (pendings.zipWithIndex.map{case (p,i) => p << i.U}).reduce(_ | _) //vector -> multi-bit
  locally {
    // Format of *topei:
    // * bits 26:16 Interrupt identity
    // * bits 10:0 Interrupt priority (same as identity)
    // * All other bit positions are zeros.
    // For detailed explainations of these memory region arguments,
    // please refer to the manual *The RISC-V Advanced Interrupt Architeture*: 3.9. Top external interrupt CSRs
    def wrap(topei: UInt): UInt = {
      val zeros = 0.U((16 - params.imsicIntSrcWidth).W)
      Cat(zeros, topei, zeros, topei)
    }
    val pv = Cat(fromCSR.addr.bits.priv.asUInt, fromCSR.addr.bits.virt)
    toCSR.topeis(0) := wrap(topeis_forEachIntFiles(0)) // m
    toCSR.topeis(1) := wrap(topeis_forEachIntFiles(1)) // s
    toCSR.topeis(2) := wrap(ParallelMux(
      UIntToOH(fromCSR.vgein - 1.U, params.geilen).asBools,
      topeis_forEachIntFiles.drop(2)
    )) // vs
  }
  val toCSR_illegal_d = RegNext((fromCSR.addr.valid | fromCSR.wdata.valid) & Seq(
    illegals_forEachIntFiles.reduce(_ | _),
    (fromCSR.wdata.valid && fromCSR.wdata.bits.op.asUInt === 0.U),
    illegal_priv
  ).reduce(_ | _))
  toCSR.illegal := toCSR_illegal_d
}

//define IMSIC_WRAP: instance one imsic when HasCVMExtention is supported, else instance two imsic modules.
class IMSIC_WRAP(
    params:    IMSICParams,
    beatBytes: Int = 4
)(implicit p: Parameters) extends Module {
  // define the ports
  val toCSR   = IO(Output(new IMSICToCSRBundle(params)))
  val fromCSR = IO(Input(new CSRToIMSICBundle(params)))
  val msiio = IO(new MSITransBundle(params))
  // define additional ports when HasCVMExtention is supported.
  val sec = if (params.HasTEEIMSIC) Some(IO(new ForCVMBundle()))
  else None // include cmode input port,and o_notice_pending output port.
  val teemsiio = if (params.HasTEEIMSIC) Some(IO(new MSITransBundle(params))) else None
  // instance module,and body logic
  private val imsic = Module(new IMSIC(params, beatBytes))
  imsic.fromCSR := fromCSR
  toCSR         := imsic.toCSR
  imsic.msiio <> msiio
  // define additional logic for sec extention
  // .foreach logic only happens when sec is not none.
  sec.foreach { secIO =>
    // get the sec.mode, connect sec.o_notice_pending to top.
    val cmode          = Wire(Bool())
    val notice_pending = Wire(Bool())
    cmode                := secIO.cmode
    secIO.notice_pending := notice_pending

    // instance tee imsic module.
    val teeimsic = Module(new IMSIC(params, beatBytes))
    teemsiio.foreach(teemsiio => teeimsic.msiio <> teemsiio)
    toCSR.rdata   := Mux(cmode, teeimsic.toCSR.rdata, imsic.toCSR.rdata) // toCSR needs to the selected depending cmode.
    toCSR.illegal := Mux(cmode, teeimsic.toCSR.illegal, imsic.toCSR.illegal)
    val s_pendings = Mux(cmode, teeimsic.toCSR.pendings(params.intFilesNum-1,1), imsic.toCSR.pendings(params.intFilesNum-1,1))
    val m_pendings = imsic.toCSR.pendings(0) // machine mode only from imsic.
    toCSR.pendings := Cat(s_pendings,m_pendings)
    //  toCSR.pendings := VecInit((0 until params.intFilesNum).map(i => pendings(i))) // uint->vector

    toCSR.topeis    := Mux(cmode, teeimsic.toCSR.topeis, imsic.toCSR.topeis)
    toCSR.topeis(0) := imsic.toCSR.topeis(0) // machine mode only from imsic.
    // to get the o_notice_pending, excluding the machine interrupt
//    val s_orpend_ree = imsic.toCSR.pendings.slice(1, params.intFilesNum) // extract the | of vector(1,N-1)
//    val s_orpend_tee = teeimsic.toCSR.pendings.slice(1, params.intFilesNum)
//    notice_pending := Mux(cmode, s_orpend_ree.reduce(_ | _), s_orpend_tee.reduce(_ | _))
    val s_orpend_ree = imsic.toCSR.pendings(params.intFilesNum-1,1) // extract the | of vector(1,N-1)
    val s_orpend_tee = teeimsic.toCSR.pendings(params.intFilesNum-1,1) //bit(params.intFilesNum-1:1)
    notice_pending   := Mux(cmode, s_orpend_ree.orR, s_orpend_tee.orR)
    teeimsic.fromCSR := fromCSR
    teeimsic.fromCSR.addr.valid := cmode & fromCSR.addr.valid // cmode=1,controls tee csr access to interrupt file indirectly
    teeimsic.fromCSR.wdata.valid := cmode & fromCSR.wdata.valid
    teeimsic.fromCSR.claims(0)   := false.B // machine interrupts are inactive for tee imsic.
    for (i <- 1 until params.privNum) {
      teeimsic.fromCSR.claims(i) := cmode & fromCSR.claims(i)
    }

    imsic.fromCSR.addr.valid := (cmode === false.B) & fromCSR.addr.valid // cmode=1,controls tee csr access to interrupt file indirectly
    imsic.fromCSR.wdata.valid := (cmode === false.B) & fromCSR.wdata.valid
    imsic.fromCSR.claims(0)   := fromCSR.claims(0) // machine interrupts are inactive for tee imsic.
    for (i <- 1 until params.privNum) {
      imsic.fromCSR.claims(i) := (cmode === false.B) & fromCSR.claims(i)
    }
  }
}

//generate TLIMSIC top module:including TLRegIMSIC_WRAP and IMSIC_WRAP
class TLIMSIC(
    params:    IMSICParams,
    beatBytes: Int = 4
//  asyncQueueParams: AsyncQueueParams
)(implicit p: Parameters) extends LazyModule with HasIMSICParameters {
  val axireg      = LazyModule(new TLRegIMSIC_WRAP(IMSICParams(HasTEEIMSIC = GHasTEEIMSIC), beatBytes))
  lazy val module = new Imp

  class Imp extends LazyModuleImp(this) {
    val toCSR         = IO(Output(new IMSICToCSRBundle(params)))
    val fromCSR       = IO(Input(new CSRToIMSICBundle(params)))
    private val imsic = Module(new IMSIC_WRAP(IMSICParams(HasTEEIMSIC = GHasTEEIMSIC), beatBytes))
    toCSR := imsic.toCSR
    imsic.fromCSR := fromCSR
    axireg.module.msiio <> imsic.msiio // msi_req/msi_ack interconnect
    // define additional ports for cvm extention
    val io_sec = if (GHasTEEIMSIC) Some(IO(new ForCVMBundle()))
    else None // include cmode input port,and o_notice_pending output port.
    /* code on when imsic has two clock domains.*/
    // --- define soc_clock for imsic bus logic ***//
    val soc_clock = IO(Input(Clock()))
    val soc_reset = IO(Input(Reset()))
    axireg.module.clock := soc_clock
    axireg.module.reset := soc_reset
    imsic.clock         := clock
    imsic.reset         := reset
    axireg.module.msiio <> imsic.msiio // msi_req/msi_ack interconnect
    // code will be compiled only when io_sec is not None.
    io_sec.foreach(iosec => imsic.sec.foreach(imsicsec => imsicsec <> iosec))
    // code will be compiled only when tee_axireg is not None.
    axireg.module.teemsiio.foreach(tee_msi_trans => imsic.teemsiio.foreach(teemsiio => tee_msi_trans <> teemsiio))
  }
}

class AXI4IMSIC(
    params:    IMSICParams,
    beatBytes: Int = 4
)(implicit p: Parameters) extends LazyModule with HasIMSICParameters {
  val axireg      = LazyModule(new AXIRegIMSIC_WRAP(IMSICParams(HasTEEIMSIC = GHasTEEIMSIC), beatBytes))
  lazy val module = new Imp
  class Imp extends LazyModuleImp(this) {
    val toCSR         = IO(Output(new IMSICToCSRBundle(params)))
    val fromCSR       = IO(Input(new CSRToIMSICBundle(params)))
    private val imsic = Module(new IMSIC_WRAP(IMSICParams(HasTEEIMSIC = GHasTEEIMSIC), beatBytes))
    toCSR := imsic.toCSR
    imsic.fromCSR := fromCSR
    axireg.module.msiio <> imsic.msiio // msi_req/msi_ack interconnect
    // define additional ports for cvm extention
    val io_sec = if (GHasTEEIMSIC) Some(IO(new ForCVMBundle()))
    else None // include cmode input port,and o_notice_pending output port.
    /* code on when imsic has two clock domains.*/
    // --- define soc_clock for imsic bus logic ***//
    val soc_clock = IO(Input(Clock()))
    val soc_reset = IO(Input(Reset()))
    axireg.module.clock := soc_clock
    axireg.module.reset := soc_reset
    imsic.clock         := clock
    imsic.reset         := reset
    // code will be compiled only when io_sec is not None.
    io_sec.foreach(iosec => imsic.sec.foreach(imsicsec => imsicsec <> iosec))
    // code will be compiled only when tee_axireg is not None.
    axireg.module.teemsiio.foreach(tee_msi_trans => imsic.teemsiio.foreach(teemsiio => tee_msi_trans <> teemsiio))
  }
}

// code below is for SEC IMSIC spec
//generate TLRegIMSIC_WRAP for IMSIC, when HasCVMExtention is supported, IMSIC is instantiated by two times,else only one
class TLRegIMSIC_WRAP(
    params:    IMSICParams,
    beatBytes: Int = 4,
    seperateBus: Boolean = false
)(implicit p: Parameters) extends LazyModule {
  // def IMSIC access TLXbar
//  require((params.HasTEEIMSIC && seperateBus) == false,
//    f"both seperateTLBus and HasTEEIMSIC are true !!")
  require(seperateBus == false,
    f"seperateTLBus is true inside TLRegIMSIC_WRAP !!")
  val axireg = LazyModule(new TLRegIMSIC(params, beatBytes)(Parameters.empty))
  val tee_axireg =

  val imsic_xbar1to2 = TLXbar()
  private val ree_sNode = TLManagerNode(Seq(TLSlavePortParameters.v1(
      managers = Seq(TLSlaveParameters.v1(
        address = Seq(
          AddressSet(params.mAddr, pow2(params.intFileMemWidth) - 1),
          AddressSet(params.sgAddr, pow2(params.intFileMemWidth) * pow2(log2Ceil(1 + params.geilen)) - 1)),
        regionType = RegionType.UNCACHED,
        executable = false,
        supportsGet = TransferSizes(1, beatBytes),
        supportsPutPartial = TransferSizes(1, beatBytes),
        supportsPutFull = TransferSizes(1, beatBytes),
        //          fifoId = Some(0)
      )),
      beatBytes = beatBytes
    )))

  private val tee_sNode =  Option.when(params.HasTEEIMSIC)(TLManagerNode(Seq(TLSlavePortParameters.v1(
    managers = Seq(TLSlaveParameters.v1(
      address = Seq(
        AddressSet(params.tee_mAddr, pow2(params.intFileMemWidth) - 1),
        AddressSet(params.tee_sgAddr, pow2(params.intFileMemWidth) * pow2(log2Ceil(1 + params.geilen)) - 1)),
      regionType = RegionType.UNCACHED,
      executable = false,
      supportsGet = TransferSizes(1, beatBytes),
      supportsPutPartial = TransferSizes(1, beatBytes),
      supportsPutFull = TransferSizes(1, beatBytes),
      //          fifoId = Some(0)
    )),
    beatBytes = beatBytes
  ))))
  ree_sNode := imsic_xbar1to2
  tee_sNode.foreach (_ := imsic_xbar1to2)
  val ree_mNode = TLClientNode(
    Seq(TLMasterPortParameters.v1(
      Seq(TLMasterParameters.v1("s_tl_", IdRange(0, 65536)))
    )))
  val tee_mNode = Option.when(params.HasTEEIMSIC)(
    TLClientNode(
      Seq(TLMasterPortParameters.v1(
        Seq(TLMasterParameters.v1("s_tl_", IdRange(0, 65536)))
      ))))
  axireg.fromMem.head := ree_mNode
  tee_mNode.foreach(tee_axireg.get.fromMem.head := _)
  lazy val module = new TLRegIMSICImp(this)
  class TLRegIMSICImp(outer: LazyModule) extends LazyModuleImp(outer) {
    val msiio = IO(Flipped(new MSITransBundle(params)))
    msiio <> axireg.module.msiio
    val teemsiio = if (params.HasTEEIMSIC) Some(IO(Flipped(new MSITransBundle(params))))
      else None // backpressure signal for axi4bus, from imsic working on cpu clock

    // code below will be compiled only when teeio is not none.
    teemsiio.foreach(teemsiio => tee_axireg.foreach(tee_axireg => teemsiio <> tee_axireg.module.msiio))
    ree_mNode.out.head._1 <> ree_sNode.in.head._1
    tee_mNode.foreach(_.out.head._1 <> tee_sNode.get.in.head._1)
  }
}

//generate AXIRegIMSIC_WRAP for IMSIC, when HasCVMExtention is supported, IMSIC is instantiated by two times,else only one
class AXIRegIMSIC_WRAP(
    params:    IMSICParams,
    beatBytes: Int = 4,
    seperateBus: Boolean = false
)(implicit p: Parameters) extends LazyModule {
  // def IMSIC access AXI4Xbar
  val imsic_xbar1to2 = AXI4Xbar()
  val ree_sNode = {
    AXI4SlaveNode(Seq(AXI4SlavePortParameters(
      slaves = Seq(AXI4SlaveParameters(
        address = Seq(
        AddressSet(params.mAddr, pow2(params.intFileMemWidth) - 1),
        AddressSet(params.sgAddr, pow2(params.intFileMemWidth) * pow2(log2Ceil(1 + params.geilen)) - 1)),
        supportsWrite = TransferSizes(1, beatBytes),
        supportsRead = TransferSizes(1, beatBytes)
      )),
      beatBytes = beatBytes
    )))
  }
 val tee_sNode =  Option.when(params.HasTEEIMSIC) {
    AXI4SlaveNode(Seq(AXI4SlavePortParameters(
      slaves = Seq(AXI4SlaveParameters(
        address = Seq(
          AddressSet(params.tee_mAddr, pow2(params.intFileMemWidth) - 1),
          AddressSet(params.tee_sgAddr, pow2(params.intFileMemWidth) * pow2(log2Ceil(1 + params.geilen)) - 1)),
        supportsWrite = TransferSizes(1, beatBytes),
        supportsRead = TransferSizes(1, beatBytes)
      )),
      beatBytes = beatBytes
    )))
  }
  ree_sNode := imsic_xbar1to2
  tee_sNode.foreach(_ := imsic_xbar1to2)
  val axireg = LazyModule(new AXIRegIMSIC(params, beatBytes)(Parameters.empty))
  //  val tee_axireg = if (params.HasTEEIMSIC) Some(LazyModule(new AXIRegIMSIC(IMSICParams(teemode = true), beatBytes)(Parameters.empty))) else None
  val tee_axireg =
    if (params.HasTEEIMSIC) Some(LazyModule(new AXIRegIMSIC(params, beatBytes)(Parameters.empty))) else None
  val ree_mNode = AXI4MasterNode(Seq(AXI4MasterPortParameters(
    Seq(AXI4MasterParameters(
      name = "s_axi_",
      id = IdRange(0, 65536)
    ))
  )))
  val tee_mNode = Option.when(params.HasTEEIMSIC) {
    AXI4MasterNode(Seq(AXI4MasterPortParameters(
      Seq(AXI4MasterParameters(
        name = "s_axi_",
        id = IdRange(0, 65536)
      ))
    )))
  }
  axireg.axi4tolite.head.node := ree_mNode
  tee_axireg.foreach(_.axi4tolite.head.node := tee_mNode.get)
  lazy val module = new AXIRegIMSICImp(this)

  class AXIRegIMSICImp(outer: LazyModule) extends LazyModuleImp(outer) {
    val msiio = IO(Flipped(new MSITransBundle(params))) // backpressure signal for axi4bus, from imsic working on cpu clock
    msiio <> axireg.module.msiio
    val teemsiio = if (params.HasTEEIMSIC) Some(IO(Flipped(new MSITransBundle(params))))
    else None // backpressure signal for axi4bus, from imsic working on cpu clock
    // code below will be compiled only when teeio is not none.
    teemsiio.foreach(teemsiio => tee_axireg.foreach(tee_axireg => teemsiio <> tee_axireg.module.msiio))
    ree_mNode.out.head._1 <> ree_sNode.in.head._1
    tee_mNode.foreach(_.out.head._1 <> tee_sNode.get.in.head._1)
  }
}

class TLRegIMSIC(
    params:      IMSICParams,
    beatBytes:   Int = 4,
    seperateBus: Boolean = false
)(implicit p: Parameters) extends LazyModule {
  val fromMem = Seq.fill(if (seperateBus) 2 else 1)(TLXbar())
  // val fromMem = LazyModule(new TLXbar).node
  private val intfileFromMems = Seq(
    AddressSet(params.mAddr, pow2(params.intFileMemWidth) - 1),
    AddressSet(params.sgAddr, pow2(params.intFileMemWidth) * pow2(log2Ceil(1 + params.geilen)) - 1)
  ).zipWithIndex.map { case (addrset, i) =>
    val intfileFromMem = TLRegMapperNode(
      address = Seq(addrset),
      beatBytes = beatBytes
    )
    intfileFromMem := (if (seperateBus) fromMem(i) else fromMem.head)
    intfileFromMem
  }

  lazy val module = new TLRegIMSICImp(this)
  class TLRegIMSICImp(outer: LazyModule) extends LazyModuleImp(outer) {
    val msiio = IO(Flipped(new MSITransBundle(params)))  // backpressure signal for axi4bus, from imsic working on cpu clock
    private val reggen = Module(new RegGen(params, beatBytes))
    // ---- instance sync fifo ----//
    // --- fifo wdata: {vector_valid,setipnum}, fifo wren: |vector_valid---//
    val FifoDataWidth = params.MSI_INFO_WIDTH
    val fifo_wdata    = Wire(Valid(UInt(FifoDataWidth.W)))

    // depth:8, data width: FifoDataWidth
    private val fifo_sync = Module(new Queue(UInt(FifoDataWidth.W), 8))
    // define about fifo write
    fifo_wdata.bits        := reggen.io.seteipnum
    fifo_wdata.valid       := reggen.io.valid
    fifo_sync.io.enq.valid := fifo_wdata.valid
    fifo_sync.io.enq.bits  := fifo_wdata.bits
    // fifo rd,controlled by msi_vld_ack from imsic working on csr clock.
    // msi_vld_ack_soc: sync result with soc clock
    val msi_vld_ack_soc = WireInit(false.B)
    val msi_vld_ack_cpu = msiio.vld_ack
    val msi_vld_req     = RegInit(false.B)
    when(params.EnableImsicAsyncBridge.B) {
      msi_vld_ack_soc := AsyncResetSynchronizerShiftReg(msi_vld_ack_cpu, 3, 0)
    }.otherwise {
      msi_vld_ack_soc := msi_vld_ack_cpu
    }
    fifo_sync.io.deq.ready := ~msi_vld_req
    // generate the msi_vld_req: high if ~empty,low when msi_vld_ack_soc
    msiio.vld_req := msi_vld_req
    val msi_vld_ack_soc_1f  = RegNext(msi_vld_ack_soc)
    val msi_vld_ack_soc_ris = msi_vld_ack_soc & (~msi_vld_ack_soc_1f)
    //    val fifo_empty = ~fifo_sync.io.deq.valid
    // msi_vld_req : high when fifo empty is false, low when ack is high. and io.deq.valid := ~empty
    when(msi_vld_ack_soc_ris) {
      msi_vld_req := false.B
    }.elsewhen(fifo_sync.io.deq.valid === true.B) {
      msi_vld_req := true.B
    }.otherwise {
      msi_vld_req := msi_vld_req
    }

    // get the msi interrupt ID info
    val msi_id_data = RegInit(0.U(params.MSI_INFO_WIDTH.W))
    val rdata_vld   = fifo_sync.io.deq.fire // assign to fifo rdata
    when(rdata_vld) { // fire: io.deq.valid & io.deq.ready
      msi_id_data := fifo_sync.io.deq.bits(params.MSI_INFO_WIDTH - 1, 0)
    }.otherwise {
      msi_id_data := msi_id_data
    }
    // port connect: io.valid is interrupt file index info.
    msiio.data := msi_id_data
    val backpress = fifo_sync.io.enq.ready
    (intfileFromMems zip reggen.regmapIOs).map {
      case (intfileFromMem, regmapIO) => intfileFromMem.regmap(regmapIO._1, regmapIO._2, backpress)
    }
  }
}

//generate axi42reg for IMSIC
class AXIRegIMSIC(
    params:      IMSICParams,
    beatBytes:   Int = 4,
    seperateBus: Boolean = false
)(implicit p: Parameters) extends LazyModule {
  val fromMem = Seq.fill(if (seperateBus) 2 else 1)(AXI4Xbar())
  val axi4tolite = Seq.fill(if (seperateBus) 2 else 1)(LazyModule(new AXI4ToLite()(Parameters.empty)))
  fromMem zip axi4tolite.map(_.node) foreach (x => x._1 := x._2)
  private val intfileFromMems = Seq(
    AddressSet(params.mAddr, pow2(params.intFileMemWidth) - 1),
    AddressSet(params.sgAddr, pow2(params.intFileMemWidth) * pow2(log2Ceil(1 + params.geilen)) - 1)
  ).zipWithIndex.map { case (addrset, i) =>
    val intfileFromMem = AXI4RegMapperNode(
      address = addrset,
      beatBytes = beatBytes
    )
    intfileFromMem := (if (seperateBus) fromMem(i) else fromMem.head)
    intfileFromMem
  }

  lazy val module = new AXIRegIMSICImp(this)
  class AXIRegIMSICImp(outer: LazyModule) extends LazyModuleImp(outer) {
    val msiio          = IO(Flipped(new MSITransBundle(params))) // backpressure signal for axi4bus, from imsic working on cpu clock
    private val reggen = Module(new RegGen(params, beatBytes))
    // ---- instance sync fifo ----//
    // --- fifo wdata: {vector_valid,setipnum}, fifo wren: |vector_valid---//
    val FifoDataWidth = params.MSI_INFO_WIDTH
    val fifo_wdata    = Wire(Valid(UInt(FifoDataWidth.W)))

    // depth:8, data width: FifoDataWidth
    private val fifo_sync = Module(new Queue(UInt(FifoDataWidth.W), 8))
    // define about fifo write
    fifo_wdata.bits        := reggen.io.seteipnum
    fifo_wdata.valid       := reggen.io.valid
    fifo_sync.io.enq.valid := fifo_wdata.valid
    fifo_sync.io.enq.bits  := fifo_wdata.bits
    // fifo rd,controlled by msi_vld_ack from imsic working on csr clock.
    // msi_vld_ack_soc: sync result with soc clock
    val msi_vld_ack_soc = WireInit(false.B)
    val msi_vld_ack_cpu = msiio.vld_ack
    val msi_vld_req     = RegInit(false.B)
    when(params.EnableImsicAsyncBridge.B) {
      msi_vld_ack_soc := AsyncResetSynchronizerShiftReg(msi_vld_ack_cpu, 3, 0)
    }.otherwise {
      msi_vld_ack_soc := msi_vld_ack_cpu
    }
    fifo_sync.io.deq.ready := ~msi_vld_req
    // generate the msi_vld_req: high if ~empty,low when msi_vld_ack_soc
    msiio.vld_req := msi_vld_req
    val msi_vld_ack_soc_1f  = RegNext(msi_vld_ack_soc)
    val msi_vld_ack_soc_ris = msi_vld_ack_soc & (~msi_vld_ack_soc_1f)
    // val fifo_empty = ~fifo_sync.io.deq.valid
    // msi_vld_req : high when fifo empty is false, low when ack is high. and io.deq.valid := ~empty
    when(msi_vld_ack_soc_ris) {
      msi_vld_req := false.B
    }.elsewhen(fifo_sync.io.deq.valid === true.B) {
      msi_vld_req := true.B
    }.otherwise {
      msi_vld_req := msi_vld_req
    }

    // get the msi interrupt ID info
    val msi_id_data = RegInit(0.U(params.MSI_INFO_WIDTH.W))
    val rdata_vld   = fifo_sync.io.deq.fire // assign to fifo rdata
    when(rdata_vld) { // fire: io.deq.valid & io.deq.ready
      msi_id_data := fifo_sync.io.deq.bits(params.MSI_INFO_WIDTH - 1, 0)
    }.otherwise {
      msi_id_data := msi_id_data
    }
    // port connect: io.valid is interrupt file index info.
    msiio.data := msi_id_data
    val backpress = fifo_sync.io.enq.ready
    (intfileFromMems zip reggen.regmapIOs).map {
      case (intfileFromMem, regmapIO) => intfileFromMem.regmap(regmapIO._1, regmapIO._2, backpress)
    }
  }
}

//integrated for async clock domain,kmh,zhaohong
class RegGen(
    params:    IMSICParams,
    beatBytes: Int = 4
) extends Module {
  val regmapIOs = Seq(
    params.intFileMemWidth,
    params.intFileMemWidth + log2Ceil(1 + params.geilen)
  ).map { width =>
    val regmapParams = RegMapperParams(width - log2Up(beatBytes), beatBytes)
    (IO(Flipped(Decoupled(new RegMapperInput(regmapParams)))), IO(Decoupled(new RegMapperOutput(regmapParams))))
  }
  // define the output reg: seteipnum is the MSI id,vld[],valid flag for interrupt file domains: m,s,vs1~vsgeilen
  val io = IO(Output(new Bundle {
    val seteipnum = UInt(params.MSI_INFO_WIDTH.W)
    val valid     = Bool()
  }))
  val valids       = WireInit(VecInit(Seq.fill(params.intFilesNum)(false.B)))
  val seteipnums   = WireInit(VecInit(Seq.fill(params.intFilesNum)(0.U(params.imsicIntSrcWidth.W))))
  val outseteipnum = RegInit(0.U(params.MSI_INFO_WIDTH.W))
  val outvalids    = RegInit(VecInit(Seq.fill(params.intFilesNum)(false.B)))

  (regmapIOs zip Seq(1, 1 + params.geilen)).zipWithIndex.map { // seq[0]: m interrupt file, seq[1]: s&vs interrupt file
    case ((regmapIO: (DecoupledIO[RegMapperInput], DecoupledIO[RegMapperOutput]), intFilesNum: Int), i: Int) =>
      {
        // j: index is 0 for m file for seq[0],index is 0~params.geilen for S intFile for seq[1]: S, G1, G2, ...
        val maps = (0 until intFilesNum).map { j =>
          val flati = i + j // seq[0]:0+0=0;seq[1]:(0~geilen)+1
          val seteipnum = WireInit(0.U.asTypeOf(Valid(UInt(params.imsicIntSrcWidth.W)))); /*for debug*/
          dontTouch(seteipnum)
          valids(flati)     := seteipnum.valid
          seteipnums(flati) := seteipnum.bits
          j * pow2(params.intFileMemWidth).toInt -> Seq(RegField(
            32,
            0.U,
            RegWriteFn { (valid, data) =>
              when(valid) { seteipnum.bits := data(params.imsicIntSrcWidth - 1, 0); seteipnum.valid := true.B }; true.B
            }
          ))
        }
        regmapIO._2 <> RegMapper(beatBytes, 1, true, regmapIO._1, maps: _*)
      }
      for (i <- 0 until params.intFilesNum) {
        when(valids(i)) {
          outseteipnum := Cat(i.U, seteipnums(i))
        }
      }
      outvalids    := valids
      io.seteipnum := outseteipnum
      io.valid     := outvalids.reduce(_ | _)
  }
}

if (params.HasTEEIMSIC) Some(LazyModule(new TLRegIMSIC(params, beatBytes)(Parameters.empty))) else None


package aia

import chisel3._
import org.chipsalliance.cde.config.{Field, Parameters}

case class IMSICParameters
(
  HasTEEIMSIC: Boolean = false
)

//trait HasIMSICParameters extends IMSICParameters
trait HasIMSICParameters {
  implicit val p: Parameters

  val IMSICParam = p(IMSICParameKey)
  val GHasTEEIMSIC = IMSICParam.HasTEEIMSIC
}

case object IMSICParameKey extends Field[IMSICParameters]


/***************************************************************************************
* Copyright (c) 2024 Beijing Institute of Open Source Chip (BOSC)
*
* ChiselAIA is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

package aia

import chisel3._
import chisel3.util._
import freechips.rocketchip.amba._
import org.chipsalliance.cde.config.Parameters
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.tilelink._
import freechips.rocketchip.amba.axi4._
import freechips.rocketchip.util._
import freechips.rocketchip.regmapper._

object pow2 { def apply(n: Int): Long = 1L << n }

class AXI4ToLite()(implicit p: Parameters) extends LazyModule {
  val node = AXI4AdapterNode( // identity
    masterFn = { mp =>
      val masters = (0 until 3).map { i =>
        AXI4MasterParameters(
          name = s"master_$i",
          id =IdRange(i * 16, (i + 1) * 16 - 1)
        )
      }.toList
      mp.copy(masters = masters)
    },
    slaveFn = { sp =>
      sp.copy(slaves = sp.slaves.map(s =>
        s.copy(address = s.address.map(a =>
          AddressSet(a.base, a.mask)))))}
  )
  lazy val module = new Impl
  class Impl extends LazyModuleImp(this) {
    (node.in zip node.out) foreach { case ((in, edgeIn), (out, edgeOut)) =>
      // out <> in
      dontTouch(in.aw.bits)
      dontTouch(in.ar.bits)
      dontTouch(in.w.bits)
      dontTouch(in.b.bits)
      dontTouch(in.r.bits)
      dontTouch(in.b.ready)
      dontTouch(in.ar.ready)
      // in.ar.bits.addr = in.ar.bits.addr.pad(32)
      // in.aw.bits.addr = in.aw.bits.addr.pad(32)
      val isAWValid = in.aw.valid
      val isARValid = in.ar.valid
      val sIDLE :: sWCH :: sBCH :: Nil =Enum(3)
      val state = RegInit(sIDLE)
      val next_state = WireInit(sIDLE)
      state := next_state
      //code about read state
      val rstate = RegInit(false.B)
      when(isARValid && (!rstate)) {
        rstate := true.B
      }.elsewhen(rstate & in.r.bits.last & in.r.ready) {
        rstate := false.B
      }.otherwise(rstate := rstate)

      val rstate_dly = RegInit(false.B)
      rstate_dly := RegNext(rstate)

      val awpulse_l = (state === sIDLE) && (next_state === sWCH)
      val arpulse_l = isARValid && (!rstate)
      val awchvld = isAWValid & in.w.valid
      val aw_l = RegEnable(in.aw.bits, awpulse_l)
      val w_l = RegEnable(in.w.bits, awpulse_l)
      // val b_l = RegEnable(in.b.bits, awpulse_l)
      val ar_l = RegEnable(in.ar.bits, arpulse_l)
      // al r_l = RegEnable(in.r.bits, awpulse_l)

      //======TODO======//
      val awcnt = RegInit(0.U(8.W)) // width of awcnt is same with awlen
      val arcnt = RegInit(0.U(8.W))
      val addrAW = aw_l.addr
      val addrAR = ar_l.addr
      // only support little-endian (0x0 is active) msi write address check for AW,refer to riscv aia spec.
      val isValidAddressAW = (addrAW(11, 0) === 0.U)
      val isValidAddressAR = (addrAR(11, 0) === 0.U)  //

      val isValidAlignmentAW = (addrAW(1, 0) === 0.U)  // Example alignment check for AW
      val isValidAlignmentAR = (addrAR(1, 0) === 0.U)  // Example alignment check for AR
      // val isAccessingValidRegisterAW = (addrAW(11, 2) === 0.U)  // Example valid register check for AW
      // val isAccessingValidRegisterAR = (addrAR(11, 2) === 0.U)  // Example valid register check for AR

      val isWAligErr  = !((aw_l.size === 2.U) & (w_l.strb === 15.U) & isValidAlignmentAW)  // alignment with 4B.
      val isWCacheErr = false.B//(aw_l.cache(3,1)).orR  //non device
      val isWLockErr = aw_l.lock      // AMO access
      val isWburstErr = false.B //aw_l.burst(1)  //0'b10 or 0'b11 : wrap or reserved
      val isWCErr = isWAligErr | isWCacheErr | isWburstErr

      val isRAligErr  = !((ar_l.size === 2.U) & isValidAlignmentAR)
      val isRCacheErr = false.B//(ar_l.cache(3,1)).orR  //non device
      val isRLockErr = ar_l.lock      // AMO access
      val isRburstErr = false.B//ar_l.burst(1)  //0'b10 or 0'b11 : wrap or reserved
      val isRCErr = isRAligErr | isRCacheErr | isRburstErr
      // val isReservedAreaAccessAW = !(isAccessingValidRegisterAW) // Reserved area for AW
      // val isReservedAreaAccessAR = !(isAccessingValidRegisterAR) // Reserved area for AR

      val isillegalAW = (!isValidAddressAW) | isWCErr | isWLockErr
      val isillegalAR = (!isValidAlignmentAR) | isRCErr | isRLockErr

      in.r.bits.last := (arcnt === ar_l.len) && in.r.valid
      val awready = RegInit(false.B) // temp signal ,out.awready for the first data, true.B for other data transaction.
      val wready = RegInit(false.B) // temp signal
      val aw_last = RegInit(false.B)
      val w_last = RegInit(false.B)
      // aw_last: the last addr for burst,
      when (state === sWCH){
        when((awcnt === aw_l.len) & awready){  //may be pulse signal
          aw_last := true.B
        }
      }.otherwise{
        aw_last := false.B
      }
      when (state === sWCH){
        when(in.w.bits.last & in.w.ready){
          w_last := true.B
        }
      }.otherwise{
        w_last := false.B
      }
      val isFinalBurst = aw_last & w_last // may be level signal ,the final data for a transaction
      val w2b_vld = (state === sWCH) & (isillegalAW | out.b.valid) & isFinalBurst
      //      val rready = out.ar.ready //ready from downstream

      //code about write state
      next_state := state
      switch(state){
        is(sIDLE) {
          when(awchvld){
            next_state := sWCH
          }
        }
        is(sWCH) {
          when(w2b_vld.asBool){ // in.b.valid can be high,only when the last burst data done and the bvalid for data to downstream is high.
            next_state := sBCH
          }
        }
        is(sBCH) {
          when(in.b.ready){
            next_state := sIDLE
          }
        }

      }
      when(state === sWCH) {
        when((!isillegalAW) & (!awready) & out.aw.ready & in.aw.valid) { // only the first illegal data to downstream
          awready := true.B
        }.elsewhen(aw_last | (awready & (awcnt === aw_l.len))) {
          awready := false.B
        }.elsewhen(isillegalAW === true.B) {
          awready := true.B
        }
      }.otherwise {
        awready := false.B
      }

      when(state === sWCH) {
        when((!isillegalAW) & (!wready) & out.w.ready & in.w.valid) { //first data
          wready := true.B
        }.elsewhen(in.w.bits.last & in.w.ready | w_last) {
          wready := false.B // legal or non first data
        }.elsewhen(isillegalAW === true.B) {
          wready := true.B
        }
      }.otherwise {
        wready := false.B
      }
      when(state === sWCH) {
        when(awcnt >= aw_l.len) { // arrive the max length of burst
          awcnt := awcnt
        }.elsewhen(awready) {
          awcnt := awcnt + 1.U
        }
      }.otherwise {
        awcnt := 0.U
      }
      when(rstate) {
        when(in.r.valid & in.r.ready) {
          arcnt := arcnt + 1.U
        }
      }.otherwise {
        arcnt := 0.U
      }
      // response for in
      val awready_dly = RegInit(false.B)
      awready_dly := awready
      val awready_ris = awready & (!awready_dly)

      in.aw.ready := awready_ris
      in.w.ready := wready
      in.b.valid := state === sBCH
      in.b.bits.id := aw_l.id
      in.b.bits.resp := Cat(isWCErr, 0.U)
      val rvalid = RegInit(false.B)
      when(rstate) {
        when(in.r.bits.last & in.r.ready) {
          rvalid := false.B
        }.otherwise {
          rvalid := true.B
        }
      }.otherwise {
        rvalid := false.B
      }
      in.r.valid := rvalid
      in.r.bits.resp := Cat(isRCErr, 0.U)
      in.r.bits.id := ar_l.id
      in.r.bits.data := 0.U
      val arready = RegInit(false.B)
      arready := arpulse_l

      in.ar.ready := arready

      // When either AW or AR is valid, perform address checks
      val m_awvalid = RegInit(false.B)
      when(awpulse_l) {
        m_awvalid := true.B
      }.elsewhen(out.aw.ready) {
        m_awvalid := false.B
      }.elsewhen(state === sIDLE) {
        m_awvalid := false.B
      }
      out.aw.valid := m_awvalid & (!isillegalAW)
      out.aw.bits.addr := aw_l.addr
      out.aw.bits.id := 0.U
      out.ar.bits.id := 0.U
      out.aw.bits.echo := aw_l.echo
      out.ar.bits.echo := ar_l.echo
      out.aw.bits.size := 2.U
      val m_wvalid = RegInit(false.B)
      when(awpulse_l) {
        m_wvalid := true.B
      }.elsewhen(out.w.ready) {
        m_wvalid := false.B
      }.elsewhen(state === sIDLE) {
        m_wvalid := false.B
      }
      out.w.valid := m_wvalid & (!isillegalAW)
      out.w.bits.strb := 15.U
      out.w.bits.data := w_l.data
      out.w.bits.last := 1.U
      val m_bready = RegInit(false.B)
      when((state === sBCH) && (next_state === sIDLE)) {
        m_bready := true.B
      }.otherwise {
        m_bready := false.B
      }
      out.b.ready := m_bready & (!isillegalAW)

      //else out signal is from the signals latched,for timing.
      }
  }
}

class AXI4Map(fn: AddressSet => BigInt)(implicit p: Parameters) extends LazyModule
{
  val node = AXI4AdapterNode( // identity
    masterFn = { mp =>
      val masters = (0 until 3).map { i =>
        AXI4MasterParameters(
          name = s"master_$i",
          id =IdRange(i * 16, (i + 1) * 16 - 1)
        )
      }.toList
      mp.copy(masters = masters)
    },
    slaveFn = { sp =>
      sp.copy(slaves = sp.slaves.map(s =>
        s.copy(address = s.address.map(a =>
          AddressSet(fn(a), a.mask)))))}
  )

  lazy val module = new Impl
  class Impl extends LazyModuleImp(this) {
    (node.in zip node.out) foreach { case ((in, edgeIn), (out, edgeOut)) =>
      out <> in
      val convert = edgeIn.slave.slaves.flatMap(_.address) zip edgeOut.slave.slaves.flatMap(_.address)
      def forward(x: UInt) =
        convert.map { case (i, o) => Mux(i.contains(x), o.base.U | (x & o.mask.U), 0.U) }.reduce(_ | _)
      def backward(x: UInt) =
        convert.map { case (i, o) => Mux(o.contains(x), i.base.U | (x & i.mask.U), 0.U) }.reduce(_ | _)

      out.aw.bits.addr := forward(in.aw.bits.addr)
      out.ar.bits.addr := forward(in.ar.bits.addr)
    }
  }
}

object AXI4Map
{
  def apply(fn: AddressSet => BigInt)(implicit p: Parameters): AXI4Node =
  {
    val map = LazyModule(new AXI4Map(fn))
    map.node
  }

}

/**
  * Convert AXI4 master to TileLink **without TLError**.
  *
  * You can use this adapter to connect external AXI4 masters to TileLink bus topology.
  *
  * Setting wcorrupt=true is insufficient to enable w.user.corrupt.
  * One must additionally list it in the AXI4 master's requestFields.
  *
  * @param wcorrupt enable AMBACorrupt in w.user
  */
class AXI4ToTLNoTLError(wcorrupt: Boolean)(implicit p: Parameters) extends LazyModule
{
  val node = AXI4ToTLNode(wcorrupt)

  lazy val module = new Impl
  class Impl extends LazyModuleImp(this) {
    (node.in zip node.out) foreach { case ((in, edgeIn), (out, edgeOut)) =>
      val numIds = edgeIn.master.endId
      val beatBytes = edgeOut.manager.beatBytes
      val beatCountBits = AXI4Parameters.lenBits + (1 << AXI4Parameters.sizeBits) - 1
      val maxFlight = edgeIn.master.masters.map(_.maxFlight.get).max
      val logFlight = log2Ceil(maxFlight)
      val txnCountBits = log2Ceil(maxFlight+1) // wrap-around must not block b_allow
      val addedBits = logFlight + 1 // +1 for read vs. write source ID

      require (edgeIn.master.masters(0).aligned)
      edgeOut.manager.requireFifo()

      // // Look for an Error device to redirect bad requests
      // val errorDevs = edgeOut.manager.managers.filter(_.nodePath.last.lazyModule.className == "TLError")
      // require (!errorDevs.isEmpty, "There is no TLError reachable from AXI4ToTL. One must be instantiated.")
      // val errorDev = errorDevs.maxBy(_.maxTransfer)
      // val error = errorDev.address.head.base
      // require (errorDev.supportsPutPartial.contains(edgeOut.manager.maxTransfer),
      //   s"Error device supports ${errorDev.supportsPutPartial} PutPartial but must support ${edgeOut.manager.maxTransfer}")
      // require (errorDev.supportsGet.contains(edgeOut.manager.maxTransfer),
      //   s"Error device supports ${errorDev.supportsGet} Get but must support ${edgeOut.manager.maxTransfer}")

      val r_out = WireDefault(out.a)
      val r_size1 = in.ar.bits.bytes1()
      val r_size = OH1ToUInt(r_size1)
      val r_ok = edgeOut.manager.supportsGetSafe(in.ar.bits.addr, r_size)
      // val r_addr = Mux(r_ok, in.ar.bits.addr, error.U | in.ar.bits.addr(log2Up(beatBytes)-1, 0))
      val r_addr = in.ar.bits.addr
      val r_count = RegInit(VecInit.fill(numIds) { 0.U(txnCountBits.W) })
      val r_id = if (maxFlight == 1) {
        Cat(in.ar.bits.id, 0.U(1.W))
      } else {
        Cat(in.ar.bits.id, r_count(in.ar.bits.id)(logFlight-1,0), 0.U(1.W))
      }

      assert (!in.ar.valid || r_size1 === UIntToOH1(r_size, beatCountBits)) // because aligned
      in.ar.ready := r_out.ready
      r_out.valid := in.ar.valid
      r_out.bits :<= edgeOut.Get(r_id, r_addr, r_size)._2

      Connectable.waiveUnmatched(r_out.bits.user, in.ar.bits.user) match {
        case (lhs, rhs) => lhs.squeezeAll :<= rhs.squeezeAll
      }

      r_out.bits.user.lift(AMBAProt).foreach { rprot =>
        rprot.privileged :=  in.ar.bits.prot(0)
        rprot.secure     := !in.ar.bits.prot(1)
        rprot.fetch      :=  in.ar.bits.prot(2)
        rprot.bufferable :=  in.ar.bits.cache(0)
        rprot.modifiable :=  in.ar.bits.cache(1)
        rprot.readalloc  :=  in.ar.bits.cache(2)
        rprot.writealloc :=  in.ar.bits.cache(3)
      }

      val r_sel = UIntToOH(in.ar.bits.id, numIds)
      (r_sel.asBools zip r_count) foreach { case (s, r) =>
        when (in.ar.fire && s) { r := r + 1.U }
      }

      val w_out = WireDefault(out.a)
      val w_size1 = in.aw.bits.bytes1()
      val w_size = OH1ToUInt(w_size1)
      val w_ok = edgeOut.manager.supportsPutPartialSafe(in.aw.bits.addr, w_size)
      // val w_addr = Mux(w_ok, in.aw.bits.addr, error.U | in.aw.bits.addr(log2Up(beatBytes)-1, 0))
      val w_addr = in.aw.bits.addr
      val w_count = RegInit(VecInit.fill(numIds) { 0.U(txnCountBits.W) })
      val w_id = if (maxFlight == 1) {
        Cat(in.aw.bits.id, 1.U(1.W))
      } else {
        Cat(in.aw.bits.id, w_count(in.aw.bits.id)(logFlight-1,0), 1.U(1.W))
      }

      assert (!in.aw.valid || w_size1 === UIntToOH1(w_size, beatCountBits)) // because aligned
      assert (!in.aw.valid || in.aw.bits.len === 0.U || in.aw.bits.size === log2Ceil(beatBytes).U) // because aligned
      in.aw.ready := w_out.ready && in.w.valid && in.w.bits.last
      in.w.ready  := w_out.ready && in.aw.valid
      w_out.valid := in.aw.valid && in.w.valid
      w_out.bits :<= edgeOut.Put(w_id, w_addr, w_size, in.w.bits.data, in.w.bits.strb)._2
      in.w.bits.user.lift(AMBACorrupt).foreach { w_out.bits.corrupt := _ }

      Connectable.waiveUnmatched(w_out.bits.user, in.aw.bits.user) match {
        case (lhs, rhs) => lhs.squeezeAll :<= rhs.squeezeAll
      }

      w_out.bits.user.lift(AMBAProt).foreach { wprot =>
        wprot.privileged :=  in.aw.bits.prot(0)
        wprot.secure     := !in.aw.bits.prot(1)
        wprot.fetch      :=  in.aw.bits.prot(2)
        wprot.bufferable :=  in.aw.bits.cache(0)
        wprot.modifiable :=  in.aw.bits.cache(1)
        wprot.readalloc  :=  in.aw.bits.cache(2)
        wprot.writealloc :=  in.aw.bits.cache(3)
      }

      val w_sel = UIntToOH(in.aw.bits.id, numIds)
      (w_sel.asBools zip w_count) foreach { case (s, r) =>
        when (in.aw.fire && s) { r := r + 1.U }
      }

      TLArbiter(TLArbiter.roundRobin)(out.a, (0.U, r_out), (in.aw.bits.len, w_out))

      val ok_b  = WireDefault(in.b)
      val ok_r  = WireDefault(in.r)

      val d_resp = Mux(out.d.bits.denied || out.d.bits.corrupt, AXI4Parameters.RESP_SLVERR, AXI4Parameters.RESP_OKAY)
      val d_hasData = edgeOut.hasData(out.d.bits)
      val d_last = edgeOut.last(out.d)

      out.d.ready := Mux(d_hasData, ok_r.ready, ok_b.ready)
      ok_r.valid := out.d.valid && d_hasData
      ok_b.valid := out.d.valid && !d_hasData

      ok_r.bits.id   := out.d.bits.source >> addedBits
      ok_r.bits.data := out.d.bits.data
      ok_r.bits.resp := d_resp
      ok_r.bits.last := d_last
      ok_r.bits.user :<= out.d.bits.user

      // AXI4 needs irrevocable behaviour
      in.r :<>= Queue.irrevocable(ok_r, 1, flow=true)

      ok_b.bits.id   := out.d.bits.source >> addedBits
      ok_b.bits.resp := d_resp
      ok_b.bits.user :<= out.d.bits.user

      // AXI4 needs irrevocable behaviour
      val q_b = Queue.irrevocable(ok_b, 1, flow=true)

      // We need to prevent sending B valid before the last W beat is accepted
      // TileLink allows early acknowledgement of a write burst, but AXI does not.
      val b_count = RegInit(VecInit.fill(numIds) { 0.U(txnCountBits.W) })
      val b_allow = b_count(in.b.bits.id) =/= w_count(in.b.bits.id)
      val b_sel = UIntToOH(in.b.bits.id, numIds)

      (b_sel.asBools zip b_count) foreach { case (s, r) =>

      }

      in.b.bits :<= q_b.bits
      in.b.valid := q_b.valid && b_allow
      q_b.ready := in.b.ready && b_allow

      // Unused channels
      out.b.ready := true.B
      out.c.valid := false.B
      out.e.valid := false.B
    }
  }
}

// object AXI4ToTL
object AXI4ToTLNoTLError
{
  def apply(wcorrupt: Boolean = true)(implicit p: Parameters) =
  {
    val axi42tl = LazyModule(new AXI4ToTLNoTLError(wcorrupt))
    axi42tl.node
  }
}

// modifications based on `rocket-chip/src/main/scala/tilelink/RegisterRouter.scala`
case class TLRegMapperNode(
  address:     Seq[AddressSet],
  beatBytes:   Int,
)(implicit valName: ValName) extends SinkNode(TLImp)(Seq(TLSlavePortParameters.v1(
  Seq(TLSlaveParameters.v1(
    address            = address,
    executable         = false,
    supportsGet        = TransferSizes(1, beatBytes),
    supportsPutPartial = TransferSizes(1, beatBytes),
    supportsPutFull    = TransferSizes(1, beatBytes),
    fifoId             = Some(0), // requests are handled in order
  )),
  beatBytes  = beatBytes,
  minLatency = 1,
))) with TLFormatNode {
  val size = 1 << log2Ceil(1 + address.map(_.max).max - address.map(_.base).min)
  require (size >= beatBytes)
  address.foreach { case a =>
    require (a.widen(size-1).base == address.head.widen(size-1).base,
      s"TLRegMapperNode addresses (${address}) must be aligned to its size ${size}")
  }

  def regmap(in: DecoupledIO[RegMapperInput], out: DecoupledIO[RegMapperOutput], backpress: Bool = true.B) = {
    val (bundleIn, edge) = this.in(0)
    val a = bundleIn.a
    val d = bundleIn.d

    in.bits.read  := a.bits.opcode === TLMessages.Get
    in.bits.index := edge.addr_hi(a.bits)
    in.bits.data  := a.bits.data
    in.bits.mask  := a.bits.mask
    Connectable.waiveUnmatched(in.bits.extra, a.bits.echo) match {
      case (lhs, rhs) => lhs :<= rhs
    }

    // copy a.bits.{source, size} to d.bits.{source, size}
    val sourceReg = RegInit(0.U.asTypeOf(a.bits.source))
    val sizeReg   = RegInit(0.U.asTypeOf(a.bits.size))
    when (a.valid) {
      sourceReg := a.bits.source
      sizeReg   := a.bits.size
    }

    // No flow control needed
    in.valid  := a.valid
    a.ready   := Mux(in.bits.read, in.ready, (backpress & in.ready))
    d.valid   := Mux(out.bits.read, out.valid, (backpress & out.valid))
    out.ready := d.ready

    // We must restore the size to enable width adapters to work
    d.bits := edge.AccessAck(toSource = sourceReg, lgSize = sizeReg)

    // avoid a Mux on the data bus by manually overriding two fields
    d.bits.data := out.bits.data
    Connectable.waiveUnmatched(d.bits.echo, out.bits.extra) match {
      case (lhs, rhs) => lhs :<= rhs
    }

    d.bits.opcode := Mux(out.bits.read, TLMessages.AccessAckData, TLMessages.AccessAck)

    // Tie off unused channels
    bundleIn.b.valid := false.B
    bundleIn.c.ready := true.B
    bundleIn.e.ready := true.B
  }
}

// modification based on `rocket-chip/src/main/scala/amba/axi4/RegisterRouter.scala`
case class AXI4RegMapperNode(
  address: AddressSet,
  beatBytes: Int = 4,
)(implicit valName: ValName) extends SinkNode(AXI4Imp)(Seq(AXI4SlavePortParameters(
  Seq(AXI4SlaveParameters(
    address       = Seq(address),
    executable    = false,
    supportsWrite = TransferSizes(1, beatBytes),
    supportsRead  = TransferSizes(1, beatBytes),
    interleavedId = Some(0))),
  beatBytes  = beatBytes,
  minLatency = 1,
))) {
  require (address.contiguous)

  // Calling this method causes the matching AXI4 bundle to be
  // configured to route all requests to the listed RegFields.
  //backpress: add by zhaohong for bus backpressure
  def regmap(in: DecoupledIO[RegMapperInput],out: DecoupledIO[RegMapperOutput], backpress: Bool = true.B) = {
    val (io, _) = this.in(0)
    val ar = io.ar
    val aw = io.aw
    val w  = io.w
    val r  = io.r
    val b  = io.b

    dontTouch(aw.bits)
    dontTouch(ar.bits)
    dontTouch(w.bits)
    dontTouch(b.bits)
    dontTouch(r.bits)

    // Prefer to execute reads first
    in.valid := ar.valid || (aw.valid && w.valid)
    ar.ready := in.ready
    aw.ready := backpress && in.ready && !ar.valid
    w .ready := backpress && in.ready && !ar.valid

    // copy {ar,aw}_bits.{echo,id} to {r,b}_bits.{echo,id}
    val arEchoReg = RegInit(0.U.asTypeOf(ar.bits.echo))
    val awEchoReg = RegInit(0.U.asTypeOf(aw.bits.echo))
    val arIdReg   = RegInit(0.U.asTypeOf(ar.bits.id))
    val awIdReg   = RegInit(0.U.asTypeOf(aw.bits.id))
    when (ar.valid) { arEchoReg := ar.bits.echo; arIdReg := ar.bits.id }
    when (aw.valid) { awEchoReg := aw.bits.echo; awIdReg := aw.bits.id }

    val addr = Mux(ar.valid, ar.bits.addr, aw.bits.addr)
    val mask = MaskGen(ar.bits.addr, ar.bits.size, beatBytes)

    in.bits.read  := ar.valid
    in.bits.index := addr >> log2Ceil(beatBytes)
    in.bits.data  := w.bits.data
    in.bits.mask  := Mux(ar.valid, mask, w.bits.strb)

    // No flow control needed
    out.ready := Mux(out.bits.read, r.ready, b.ready)
    r.valid := out.valid &&  out.bits.read
    b.valid := backpress && out.valid && !out.bits.read // backpressure for write operation.

    r.bits.id   := arIdReg
    r.bits.data := out.bits.data
    r.bits.last := true.B
    r.bits.resp := AXI4Parameters.RESP_OKAY
    r.bits.echo :<= arEchoReg

    b.bits.id   := awIdReg
    b.bits.resp := AXI4Parameters.RESP_OKAY
    b.bits.echo :<= awEchoReg
  }
}