package lib
import chisel3._
import chisel3.util._
import include._
trait lib extends param{
  implicit def int2boolean(b:Int) = if (b==1) true else false

  implicit def uint2bool(b:UInt) = b.asBool()

  implicit def aslong(b:Int) = 0xFFFFFFFFL & b

  def repl(b:Int, a:UInt) = VecInit.tabulate(b)(i => a).reduce(Cat(_,_))

  def bridge_gen(tag: Int, ahb_type: Boolean) = if(BUILD_AXI4) flip(tag, ahb_type) else ahb_bridge_gen(ahb_type)

  def flip(tag: Int , ahb_type: Boolean) = if(ahb_type) Flipped(new axi_channels(tag)) else new axi_channels(tag)

  def ahb_bridge_gen(ahb_type: Boolean) = if(ahb_type) new Bundle{
    val sig = Flipped(new ahb_channel())
    val hsel = Input(Bool())
    val hreadyin = Input(Bool())}
  else new ahb_channel()

  def MEM_CAL : (Int, Int, Int, Int)=
    (ICACHE_WAYPACK, ICACHE_ECC) match{
      case(0,0) => (68, 22, 68, 22)
      case(0,1)  => (71, 26, 71, 26)
      case(1,0)  => (68*ICACHE_NUM_WAYS, 22*ICACHE_NUM_WAYS, 68, 22)
      case(1,1)   => (71*ICACHE_NUM_WAYS, 26*ICACHE_NUM_WAYS, 71, 26)
    }

  val DATA_MEM_LINE = MEM_CAL
  val Tag_Word = MEM_CAL._4


  object rvsyncss {
    def apply(din:UInt,clk:Clock) =withClock(clk){RegNext(withClock(clk){RegNext(din,0.U)},0.U)}
  }
  object rvsyncss_fpga {
    def apply(din:UInt, gw_clk:Clock, rawclk:Clock, clken:Bool) = {
      val din_ff1 = rvdff_fpga(din,gw_clk,clken, rawclk)
      rvdff_fpga(din_ff1,gw_clk,clken, rawclk)
    }
  }

  ///////////////////////////////////////////////////////////////////
  def btb_tag_hash(pc : UInt) =
    VecInit.tabulate(3)(i => pc(BTB_ADDR_HI-1+((i+1)*(BTB_BTAG_SIZE)),BTB_ADDR_HI+(i*BTB_BTAG_SIZE))).reduce(_^_)

  ///////////////////////////////////////////////////////////////////
  def btb_tag_hash_fold(pc : UInt) =
    pc(BTB_ADDR_HI+(2*BTB_BTAG_SIZE),BTB_ADDR_HI+BTB_BTAG_SIZE+1)^pc(BTB_ADDR_HI+BTB_BTAG_SIZE,BTB_ADDR_HI+1)

  ///////////////////////////////////////////////////////////////////
  def btb_addr_hash(pc : UInt) =
    if(BTB_FOLD2_INDEX_HASH) pc(BTB_INDEX1_HI-1,BTB_INDEX1_LO-1) ^ pc(BTB_INDEX3_HI-1,BTB_INDEX3_LO-1)
    else (pc(BTB_INDEX1_HI-1,BTB_INDEX1_LO-1) ^ pc(BTB_INDEX2_HI-1,BTB_INDEX2_LO-1) ^ pc(BTB_INDEX3_HI-1,BTB_INDEX3_LO-1))

  ///////////////////////////////////////////////////////////////////
  def btb_ghr_hash(hashin : UInt, ghr :UInt) =
    if(BHT_GHR_HASH_1) Cat(ghr(BHT_GHR_SIZE-1,BTB_INDEX1_HI-1), hashin(BTB_INDEX1_HI,2) ^ ghr(BTB_INDEX1_HI-2,0))
    else hashin(BHT_GHR_SIZE+1,2) ^ ghr(BHT_GHR_SIZE-1,0)

  ///////////////////////////////////////////////////////////////////
  def rveven_paritycheck(data_in:UInt, parity_in:UInt) : UInt =
    (data_in.xorR.asUInt) ^ parity_in

  ///////////////////////////////////////////////////////////////////
  def rveven_paritygen(data_in : UInt) =
    data_in.xorR.asUInt
  ///////////////////////////////////////////////////////////////////
  //rvbradder(Cat(pc, 0.U), Cat(offset, 0.U))
  def rvbradder (pc:UInt, offset:UInt) = {
    val dout_lower = pc(12,1) +& offset(12,1)
    val pc_inc = pc(31,13)+1.U
    val pc_dec = pc(31,13)-1.U
    val sign = offset(12)
    Cat(Mux1H(Seq(( sign ^ !dout_lower(dout_lower.getWidth-1)).asBool -> pc(31,13),
      (!sign &  dout_lower(dout_lower.getWidth-1)).asBool -> pc_inc,
      ( sign & !dout_lower(dout_lower.getWidth-1)).asBool -> pc_dec)), dout_lower(11,0), 0.U)
  }

  ///////////////////////////////////////////////////////////////////
  // RV range
  def rvrangecheck(CCM_SADR:Int, CCM_SIZE:Int, addr:UInt) = {
    val REGION_BITS = 4;
    val MASK_BITS = 10 + log2Ceil(CCM_SIZE)
    val start_addr = aslong(CCM_SADR).U(32.W)
    val region = start_addr(31,32-REGION_BITS)
    val in_region = addr(31,(32-REGION_BITS)) === region
    val in_range = if(CCM_SIZE==48)
      (addr(31, MASK_BITS) === start_addr(31,MASK_BITS)) & ~addr(MASK_BITS-1 , MASK_BITS-2).andR
    else addr(31,MASK_BITS) === start_addr(31,MASK_BITS)
    (in_region, in_range)
  }

  def rvlsadder(rs1:UInt,offset:UInt) = {
    val w1 =  Cat(0.U(1.W),rs1(11,0)) + Cat(0.U(1.W),offset(11,0))  //w1[12] =cout  offset[11]=sign
    val dout_upper = ((Fill(20, ~(offset(11) ^ w1(12)))) & rs1(31,12)) |
      ((Fill(20, ~offset(11) & w1(12))) & (rs1(31,12)+1.U)) |
      ((Fill(20, offset(11) & ~w1(12))) & (rs1(31,12)-1.U))
    Cat(dout_upper,w1(11,0))
  }
  ///////////////////////////////////////////////////////////////////
  def rvmaskandmatch(mask:UInt, data:UInt, masken:Bool):UInt={
    val matchvec = Wire(Vec(data.getWidth,UInt(1.W)))
    val masken_or_fullmask = masken & ~mask.andR
    matchvec(0)  :=  masken_or_fullmask | (mask(0) === data(0)).asUInt
    for(i <- 1 to data.getWidth-1)
      matchvec(i) := Mux(mask(i-1,0).andR & masken_or_fullmask,"b1".U,(mask(i) === data(i)).asUInt)
    matchvec.asUInt.andR()
  }

  ///////////////////////////////////////////////////////////////////
  def configurable_gw(gw_clk : Clock, rawclk:Clock, clken:Bool, rst:AsyncReset, extintsrc_req_sync : Bool, meigwctrl_polarity: Bool, meigwctrl_type: Bool, meigwclr: Bool)  = {
    val gw_int_pending = WireInit(UInt(1.W),0.U)
    val gw_int_pending_in =  (extintsrc_req_sync ^ meigwctrl_polarity) | (gw_int_pending & !meigwclr)
    gw_int_pending := rvdff_fpga(gw_int_pending_in,gw_clk,clken,rawclk)
    Mux(meigwctrl_type.asBool(), ((extintsrc_req_sync ^  meigwctrl_polarity) | gw_int_pending), (extintsrc_req_sync ^  meigwctrl_polarity))
  }

  ///////////////////////////////////////////////////////////////////
  // Move rvecc_encode to a proper trait
  def rvecc_encode(din:UInt):UInt = {
    def pat(y : List[Int]) = (0 until y.size).map(i=> din(y(i))).reduce(_^_)
    val w0 = pat(List(0, 1, 3, 4, 6, 8, 10, 11, 13, 15, 17, 19, 21, 23, 25, 26, 28, 30))
    val w1 = pat(List(0, 2, 3, 5, 6, 9, 10, 12, 13, 16, 17, 20, 21, 24, 25, 27, 28, 31))
    val w2 = pat(List(1, 2, 3, 7, 8, 9, 10, 14, 15, 16, 17, 22, 23, 24, 25, 29, 30, 31))
    val w3 = pat(List(4, 5, 6, 7, 8, 9, 10, 18, 19, 20, 21, 22, 23, 24, 25))
    val w4 = pat(List(11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25))
    val w5 = pat(List(26, 27, 28, 29, 30, 31))
    val w6 = Cat(w5,w4,w3,w2,w1,w0)
    Cat(din.xorR ^ w6.xorR, w6)
  }

  class rvecc_encode extends Module{   //Done for verification and testing
    val io      =  IO(new Bundle{
      val din     =  Input(UInt(32.W))
      val ecc_out =  Output(UInt(7.W))
    })
    val mask0 = Array(1,1,0,1,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,0,1,0,1,0)
    val mask1 = Array(1,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,1,1,0,0,1)
    val mask2 = Array(0,1,1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,1,1,1)
    val mask3 = Array(0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0)
    val mask4 = Array(0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0)
    val mask5 = Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1)

    val w0 = Wire(Vec(18,UInt(1.W)))
    val w1 = Wire(Vec(18,UInt(1.W)))
    val w2 = Wire(Vec(18,UInt(1.W)))
    val w3 = Wire(Vec(15,UInt(1.W)))
    val w4 = Wire(Vec(15,UInt(1.W)))
    val w5 = Wire(Vec(6, UInt(1.W)))
    var j = 0;var k = 0;var m = 0;
    var x = 0;var y = 0;var z = 0;

    for(i <- 0 to 31)
    {
      if(mask0(i)==1) {w0(j) := io.din(i); j = j +1 }
      if(mask1(i)==1) {w1(k) := io.din(i); k = k +1 }
      if(mask2(i)==1) {w2(m) := io.din(i); m = m +1 }
      if(mask3(i)==1) {w3(x) := io.din(i); x = x +1 }
      if(mask4(i)==1) {w4(y) := io.din(i); y = y +1 }
      if(mask5(i)==1) {w5(z) := io.din(i); z = z +1 }
    }
    val w6 = Cat((w5.asUInt.xorR),(w4.asUInt.xorR),(w3.asUInt.xorR),(w2.asUInt.xorR),(w1.asUInt.xorR),(w0.asUInt.xorR))
    io.ecc_out := Cat(io.din.xorR ^ w6.xorR, w6)
  }


  def rvecc_decode(en:UInt,din:UInt,ecc_in:UInt,sed_ded:UInt)= {
    val mask0 = Array(1,1,0,1,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,0,1,0,1,0)
    val mask1 = Array(1,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,1,1,0,0,1)
    val mask2 = Array(0,1,1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,1,1,1)
    val mask3 = Array(0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0)
    val mask4 = Array(0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0)
    val mask5 = Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1)

    val w0 = Wire(Vec(18,UInt(1.W)))
    val w1 = Wire(Vec(18,UInt(1.W)))
    val w2 = Wire(Vec(18,UInt(1.W)))
    val w3 = Wire(Vec(15,UInt(1.W)))
    val w4 = Wire(Vec(15,UInt(1.W)))
    val w5 = Wire(Vec(6,UInt(1.W)))

    var j = 0;var k = 0;var m = 0;  var n =0;
    var x = 0;var y = 0;

    for(i <- 0 to 31)
    {
      if(mask0(i)==1) {w0(j) := din(i); j = j +1 }
      if(mask1(i)==1) {w1(k) := din(i); k = k +1 }
      if(mask2(i)==1) {w2(m) := din(i); m = m +1 }
      if(mask3(i)==1) {w3(n) := din(i); n = n +1 }
      if(mask4(i)==1) {w4(x) := din(i); x = x +1 }
      if(mask5(i)==1) {w5(y) := din(i); y = y +1 }
    }

    val ecc_check = Cat((din.xorR ^ ecc_in.xorR) & ~sed_ded ,ecc_in(5)^(w5.asUInt.xorR),ecc_in(4)^(w4.asUInt.xorR),ecc_in(3)^(w3.asUInt.xorR),ecc_in(2)^(w2.asUInt.xorR),ecc_in(1)^(w1.asUInt.xorR),ecc_in(0)^(w0.asUInt.xorR))
    val single_ecc_error =  en & (ecc_check=/= 0.U) & ecc_check(6)
    val double_ecc_error =  en & (ecc_check=/= 0.U) & ~ecc_check(6)
    val error_mask = Wire(Vec(39,UInt(1.W)))

    for(i <- 1 until 40){
      error_mask(i-1) := ecc_check(5,0) === i.asUInt
    }
    val din_plus_parity = Cat(ecc_in(6), din(31,26), ecc_in(5), din(25,11), ecc_in(4), din(10,4), ecc_in(3), din(3,1), ecc_in(2), din(0), ecc_in(1,0))
    val dout_plus_parity = Mux(single_ecc_error.asBool, (error_mask.asUInt ^ din_plus_parity), din_plus_parity)

    val dout =  Cat(dout_plus_parity(37,32),dout_plus_parity(30,16), dout_plus_parity(14,8), dout_plus_parity(6,4), dout_plus_parity(2))
    val ecc_out = Cat(dout_plus_parity(38) ^ (ecc_check(6,0) === "b1000000".U(7.W)), dout_plus_parity(31), dout_plus_parity(15), dout_plus_parity(7), dout_plus_parity(3), dout_plus_parity(1,0))
    (ecc_out,dout,single_ecc_error,double_ecc_error)
  }

  class rvecc_encode_64 extends Module{    //Done for verification and testing
    val io      =  IO(new Bundle{
      val din     =  Input(UInt(64.W))
      val ecc_out =  Output(UInt(7.W))
    })
    val mask0 = Array(1,1,0,1,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,0,1,0,1,0,1)
    val mask1 = Array(1,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,1,1,0,0,1,1)
    val mask2 = Array(0,1,1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,1,1,1,1)
    val mask3 = Array(0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0)
    val mask4 = Array(0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0)
    val mask5 = Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0)
    val mask6 = Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1)

    val w0 = Wire(Vec(35,UInt(1.W)))
    val w1 = Wire(Vec(35,UInt(1.W)))
    val w2 = Wire(Vec(35,UInt(1.W)))
    val w3 = Wire(Vec(31,UInt(1.W)))
    val w4 = Wire(Vec(31,UInt(1.W)))
    val w5 = Wire(Vec(31,UInt(1.W)))
    val w6 = Wire(Vec(7, UInt(1.W)))

    var j = 0;var k = 0;var m = 0; var n =0;
    var x = 0;var y = 0;var z = 0

    for(i <- 0 to 63)
    {
      if(mask0(i)==1) {w0(j) := io.din(i); j = j +1 }
      if(mask1(i)==1) {w1(k) := io.din(i); k = k +1 }
      if(mask2(i)==1) {w2(m) := io.din(i); m = m +1 }
      if(mask3(i)==1) {w3(n) := io.din(i); n = n +1 }
      if(mask4(i)==1) {w4(x) := io.din(i); x = x +1 }
      if(mask5(i)==1) {w5(y) := io.din(i); y = y +1 }
      if(mask6(i)==1) {w6(z) := io.din(i); z = z +1 }
    }
    io.ecc_out := Cat((w6.asUInt.xorR),(w5.asUInt.xorR),(w4.asUInt.xorR),(w3.asUInt.xorR),(w2.asUInt.xorR),(w1.asUInt.xorR),(w0.asUInt.xorR))
  }

  def rvecc_encode_64(din:UInt):UInt = {
    val mask0 = Array(1,1,0,1,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,0,1,0,1,0,1)
    val mask1 = Array(1,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,1,1,0,0,1,1)
    val mask2 = Array(0,1,1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,1,1,1,1)
    val mask3 = Array(0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0)
    val mask4 = Array(0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0)
    val mask5 = Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0)
    val mask6 = Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1)

    val w0 = Wire(Vec(35,UInt(1.W)))
    val w1 = Wire(Vec(35,UInt(1.W)))
    val w2 = Wire(Vec(35,UInt(1.W)))
    val w3 = Wire(Vec(31,UInt(1.W)))
    val w4 = Wire(Vec(31,UInt(1.W)))
    val w5 = Wire(Vec(31,UInt(1.W)))
    val w6 = Wire(Vec(7, UInt(1.W)))

    var j = 0;var k = 0;var m = 0; var n =0;
    var x = 0;var y = 0;var z = 0

    for(i <- 0 to 63)
    {
      if(mask0(i)==1) {w0(j) := din(i); j = j +1 }
      if(mask1(i)==1) {w1(k) := din(i); k = k +1 }
      if(mask2(i)==1) {w2(m) := din(i); m = m +1 }
      if(mask3(i)==1) {w3(n) := din(i); n = n +1 }
      if(mask4(i)==1) {w4(x) := din(i); x = x +1 }
      if(mask5(i)==1) {w5(y) := din(i); y = y +1 }
      if(mask6(i)==1) {w6(z) := din(i); z = z +1 }
    }
    Cat((w6.asUInt.xorR),(w5.asUInt.xorR),(w4.asUInt.xorR),(w3.asUInt.xorR),(w2.asUInt.xorR),(w1.asUInt.xorR),(w0.asUInt.xorR))
  }

  def rvecc_decode_64(en:UInt,din:UInt,ecc_in:UInt) = {
    val mask0 = Array(1,1,0,1,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,0,1,0,1,0,1)
    val mask1 = Array(1,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,1,1,0,0,1,1)
    val mask2 = Array(0,1,1,1,0,0,0,1,1,1,1,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,1,1,1,1)
    val mask3 = Array(0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0)
    val mask4 = Array(0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0)
    val mask5 = Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0)
    val mask6 = Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1)

    val w0 = Wire(Vec(35,UInt(1.W)))
    val w1 = Wire(Vec(35,UInt(1.W)))
    val w2 = Wire(Vec(35,UInt(1.W)))
    val w3 = Wire(Vec(31,UInt(1.W)))
    val w4 = Wire(Vec(31,UInt(1.W)))
    val w5 = Wire(Vec(31,UInt(1.W)))
    val w6 = Wire(Vec(7, UInt(1.W)))

    var j = 0;var k = 0;var m = 0;  var n =0;
    var x = 0;var y = 0;var z = 0

    for(i <- 0 to 63)
    {
      if(mask0(i)==1) {w0(j) := din(i); j = j +1 }
      if(mask1(i)==1) {w1(k) := din(i); k = k +1 }
      if(mask2(i)==1) {w2(m) := din(i); m = m +1 }
      if(mask3(i)==1) {w3(n) := din(i); n = n +1 }
      if(mask4(i)==1) {w4(x) := din(i); x = x +1 }
      if(mask5(i)==1) {w5(y) := din(i); y = y +1 }
      if(mask6(i)==1) {w6(z) := din(i); z = z +1 }
    }

    val ecc_check = Cat((ecc_in(6) ^ w6.asUInt.xorR) ,ecc_in(5)^(w5.asUInt.xorR),ecc_in(4)^(w4.asUInt.xorR),ecc_in(3)^(w3.asUInt.xorR),ecc_in(2)^(w2.asUInt.xorR),ecc_in(1)^(w1.asUInt.xorR),ecc_in(0)^(w0.asUInt.xorR))
    val ecc_error = en & (ecc_check(6,0) =/= 0.U)
    ecc_error
  }



  class gated_latch extends BlackBox with HasBlackBoxResource {
    val io = IO(new Bundle {
      val Q = Output(Clock())
      val CK  = Input(Clock())
      val EN  = Input(Bool())
      val SE = Input(Bool())
    })
    addResource("/vsrc/gated_latch.sv")
  }

  class rvclkhdr extends Module {
    val io = IO(new Bundle {
      val l1clk = Output(Clock())
      val clk  = Input(Clock())
      val en  = Input(Bool())
      val scan_mode = Input(Bool())
    })
    val clkhdr = { Module(new gated_latch) }
    io.l1clk := clkhdr.io.Q
    clkhdr.io.CK := io.clk
    clkhdr.io.EN := io.en
    clkhdr.io.SE := io.scan_mode
  }

  object rvclkhdr {
    def apply(clk: Clock, en: Bool, scan_mode: Bool): Clock = {
      val cg = Module(new rvclkhdr)
      cg.io.clk := clk
      cg.io.en := en
      cg.io.scan_mode := 0.U
      cg.io.l1clk
    }
  }
  object rvoclkhdr {
    def apply(clk: Clock, en: Bool, scan_mode: Bool): Clock = {
      if(RV_FPGA_OPTIMIZE){
        clk
      }else{
        val cg = Module(new rvclkhdr)
        cg.io.clk := clk
        cg.io.en := en
        cg.io.scan_mode := 0.U
        cg.io.l1clk
      }

    }
  }

  def rvrangecheck_ch(addr:UInt,CCM_SADR:UInt, CCM_SIZE:Int=128) = {
    val REGION_BITS = 4
    val MASK_BITS   = 10 + log2Ceil(CCM_SIZE)
    val start_addr  = CCM_SADR
    val region  = start_addr(31,(32-REGION_BITS))
    val in_region  = (addr(31,(32-REGION_BITS)) === region(REGION_BITS-1,0)).asUInt
    val in_range   = Wire(UInt(1.W))
    if(CCM_SIZE == 48)
      in_range := (addr(31,MASK_BITS) === start_addr(31,MASK_BITS)).asUInt & ~(addr(MASK_BITS-1,MASK_BITS-2).andR.asUInt)
    else
      in_range := (addr(31,MASK_BITS) === start_addr(31,MASK_BITS)).asUInt
    (in_range,in_region)
  }
  object rvdff_fpga {
    def apply(din: UInt, clk: Clock, clken: Bool,rawclk:Clock) = {
      if (RV_FPGA_OPTIMIZE)
        withClock(rawclk) {RegEnable (din, 0.U, clken)}
      else withClock(clk) {RegNext (din, 0.U)}
    }
    def apply(din: Bool, clk: Clock, clken: Bool,rawclk:Clock) = {
      if (RV_FPGA_OPTIMIZE)
        withClock(rawclk) {RegEnable (din, 0.B, clken)}
      else withClock(clk) {RegNext (din, 0.B)}
    }
  }
  object rvdffs_fpga {
    def apply(din: UInt, en:Bool,clk: Clock, clken: Bool,rawclk:Clock) = {
      if (RV_FPGA_OPTIMIZE)
        withClock (rawclk) {RegEnable (din, 0.U, (clken & en))}
      else withClock(clk) {RegEnable (din, 0.U,en)}
    }
  }
  object rvdffsc_fpga {
    def apply(din: UInt, en:Bool,clear: UInt, clk: Clock, clken: Bool,rawclk:Clock) = {
      val dout =Wire(UInt())
      if (RV_FPGA_OPTIMIZE)
        dout := withClock (rawclk) {RegEnable ((din & Fill(din.getWidth,!clear)), 0.U, ((en|clear)& clken))}
      else dout := withClock(clk) {RegNext (Mux(en,din,dout) & !clear, 0.U)}
      dout
    }
    def apply(din: Bool, en:Bool,clear: UInt, clk: Clock, clken: Bool,rawclk:Clock) = {
      val dout =Wire(Bool())
      if (RV_FPGA_OPTIMIZE)
        dout := withClock (rawclk) {RegEnable ((din & Fill(din.getWidth,!clear)), 0.B, ((en|clear)& clken))}
      else dout := withClock(clk) {RegNext (Mux(en,din,dout) & !clear, 0.B)}
      dout
    }
  }
  ////rvdffe ///////////////////////////////////////////////////////////////////////
  object rvdffe {
    def apply(din: UInt, en: Bool, clk: Clock, scan_mode: Bool): UInt = {
      val obj = Module(new rvclkhdr())
      val l1clk = obj.io.l1clk
      obj.io.clk := clk
      obj.io.en := en
      obj.io.scan_mode := 0.U
      if(RV_FPGA_OPTIMIZE)
        withClock(clk){RegEnable(din,0.U,en)}
      else
        withClock(l1clk) {
          RegNext(din, 0.U)
        }
    }
    def apply(din: Bundle, en: Bool, clk: Clock, scan_mode: Bool) = {
      val obj = Module(new rvclkhdr())
      val l1clk = obj.io.l1clk
      obj.io.clk := clk
      obj.io.en := en
      obj.io.scan_mode := 0.U
      if(RV_FPGA_OPTIMIZE)
        withClock(clk){RegEnable(din,0.U.asTypeOf(din),en)}
      else
        withClock(l1clk) {
          RegNext(din, 0.U.asTypeOf(din))
        }

    }
    def apply(din: SInt, en: Bool, clk: Clock, scan_mode: Bool): Bits with Num[_ >: SInt with UInt <: Bits with Num[_ >: SInt with UInt]] = {
      val obj = Module(new rvclkhdr())
      val l1clk = obj.io.l1clk
      obj.io.clk := clk
      obj.io.en := en
      obj.io.scan_mode := 0.U
      if(RV_FPGA_OPTIMIZE)
        withClock(clk){RegEnable(din,0.S,en)}
      else
        withClock(l1clk) {
          RegNext(din, 0.S)
        }
    }
  }
  ////////////////////////////////////////////////////////////////////////////////////
  object rvdffie {
    def apply(din: UInt, clk: Clock, rst_l: AsyncReset, scan_mode: Bool)= {
      val dout = WireInit(UInt(), 0.U)
      val en = (din ^ dout).orR
      if (RV_FPGA_OPTIMIZE) {
        withClock(clk) {
          dout := RegEnable(din, 0.U, en)
        }
        dout
      } else {

        val obj = Module(new rvclkhdr())
        val l1clk = obj.io.l1clk
        obj.io.clk := clk
        obj.io.en := en
        obj.io.scan_mode := scan_mode
        withClock(l1clk) {
          dout := RegNext(din, 0.U)
        }
        dout
      }

    }
    def apply(din: Bool, clk: Clock, rst_l: AsyncReset, scan_mode: Bool)= {
      val dout = WireInit(Bool(), 0.B)
      val en = (din ^ dout).orR
      if (RV_FPGA_OPTIMIZE) {
        withClock(clk) {
          dout := RegEnable(din, 0.B, en)
        }
        dout
      } else {

        val obj = Module(new rvclkhdr())
        val l1clk = obj.io.l1clk
        obj.io.clk := clk
        obj.io.en := en
        obj.io.scan_mode := scan_mode
        withClock(l1clk) {
          dout := RegNext(din, 0.B)
        }
        dout
      }
    }

    def apply(din: Bundle, clk: Clock, rst_l: AsyncReset, scan_mode: Bool) = {
      val dout = WireInit(din)
      val port = din.getElements
      val port2 = dout.getElements
      val en = (port zip port2).map { case (in, out) => (in.asUInt ^ out.asUInt).orR }.reduce(_ | _)
      if (RV_FPGA_OPTIMIZE) {
        withClock(clk) {
          dout := RegEnable(din, 0.U.asTypeOf(din.cloneType), en)
        }
        dout
      } else {
        val obj = Module(new rvclkhdr())
        val l1clk = obj.io.l1clk
        obj.io.clk := clk
        obj.io.en := en
        obj.io.scan_mode := scan_mode
        withClock(l1clk) {
          dout := RegNext(din, 0.U.asTypeOf(din.cloneType))
        }
        dout
      }

    }

    def apply(din: Vec[UInt], clk: Clock, rst_l: AsyncReset, scan_mode: Bool) = {
      val dout = WireInit(din)
      val port = din.getElements
      val port2 = dout.getElements
      val en = (port zip port2).map { case (in, out) => (in.asUInt ^ out.asUInt).orR }.reduce(_ | _)
      if (RV_FPGA_OPTIMIZE) {
        withClock(clk) {
          dout := RegEnable(din, 0.U.asTypeOf(din), en)
        }
        dout
      } else {
        val obj = Module(new rvclkhdr())
        val l1clk = obj.io.l1clk
        obj.io.clk := clk
        obj.io.en := en
        obj.io.scan_mode := scan_mode
        withClock(l1clk) {
          dout := RegNext(din, 0.U.asTypeOf(din.cloneType))
        }
        dout
      }
    }
  }
  /////////////////////////////////////////////////////////////////////////////////////////////
  object rvdffiee {
    def apply(din: UInt, clk: Clock, rst_l: AsyncReset, en: Bool, scan_mode: Bool) = {
      val final_en = Wire(Bool())
      val dout = WireInit(UInt(), 0.U)
      if (RV_FPGA_OPTIMIZE) {
        withClock(clk) {
          dout := RegEnable(din, 0.U.asTypeOf(din), en)
        }
        dout
      } else {
        final_en := (din ^ dout).orR & en
        dout := rvdffe(din, final_en, clk, scan_mode)
        dout
      }
    }

    def apply(din: Bundle, clk: Clock, rst_l: AsyncReset, en: Bool, scan_mode: Bool) = {
      val dout = WireInit(din)
      val port = din.getElements
      val port2 = dout.getElements
      if (RV_FPGA_OPTIMIZE) {
        withClock(clk) {
          dout := RegEnable(din, 0.U.asTypeOf(din), en)
        }
        dout
      } else {
        val final_en = Wire(Bool())

        final_en := (port zip port2).map { case (in, out) => in.asUInt ^ out.asUInt }.reduce(_ | _) & en
        //      final_en := (din ^ rvdffe(din,final_en,clk,scan_mode)).orR & en
        dout := rvdffe(din, final_en, clk, scan_mode)
        dout
      }
    }
  }
  ////////////////////////////////////////////////////////////////////////////////////////////////////
  // special power flop for predict packet
  // format: { LEFT, RIGHT==31 }
  // LEFT # of bits will be done with rvdffe; RIGHT is enabled by LEFT[LSB] & en

  ////////////////////////////////////////////////////////////////////////////////////////////////////////
  def rvdffppe_UInt(din: UInt, clk: Clock, rst_l: AsyncReset, en : Bool, scan_mode: Bool, WIDTH: Int=32) = {
    val RIGHT = 31
    val LEFT = WIDTH - RIGHT
    val LMSB = WIDTH-1
    val LLSB = LMSB-LEFT+1
    val RMSB = LLSB-1
    val RLSB = LLSB-RIGHT
    if(RV_FPGA_OPTIMIZE){
      withClock(clk){
        RegEnable(din,0.U.asTypeOf(din),en)
      }
    }else
      Cat(rvdffe(din(LMSB,LLSB),en,clk,scan_mode),rvdffe(din(RMSB,RLSB),(en&din(LLSB)).asBool,clk,scan_mode))

  }
  object rvdffppe {
    def apply(din: Bundle, clk: Clock, rst_l: AsyncReset, en : Bool, scan_mode: Bool, elements: Int,en_bit :Bool) = {
      if(RV_FPGA_OPTIMIZE){
        withClock(clk){
          RegEnable(din,0.U.asTypeOf(din),en)
        }
      }
      else{
        val vec = MixedVecInit((0 until din.getElements.length).map(i=>
          if(i<=elements) rvdffe(din.getElements(i).asUInt(),en,clk,scan_mode)
          else rvdffe(din.getElements(i).asUInt(),(en& en_bit).asBool,clk,scan_mode)))

        vec.asTypeOf(din)
      }
    }
  }

  ////////////////////////////////////////////////////////////////////////////////////////////////////////
  def rvdfflie_UInt(din: UInt, clk: Clock, rst_l: AsyncReset, en : Bool, scan_mode: Bool, WIDTH: Int=16, LEFT: Int=8) = {
    val EXTRA = WIDTH-LEFT
    val LMSB = WIDTH-1
    val LLSB = LMSB-LEFT+1
    val XMSB = LLSB-1
    val XLSB = LLSB-EXTRA
    if(RV_FPGA_OPTIMIZE){
      withClock(clk){
        RegEnable(din,0.U.asTypeOf(din),en)
      }
    }else
      Cat(rvdffiee(din(LMSB,LLSB),clk,rst_l,en,scan_mode),rvdffe(din(XMSB,XLSB),en,clk,scan_mode))

  }
  object rvdfflie {
    def apply(din: Bundle, clk: Clock, rst_l: AsyncReset, en : Bool, scan_mode: Bool, elements: Int) = {
      if(RV_FPGA_OPTIMIZE){
        withClock(clk){
          RegEnable(din,0.U.asTypeOf(din),en)
        }
      }
      else{
        val vec = MixedVecInit((0 until din.getElements.length).map(i=>
          if(i<=elements) rvdffe(din.getElements(i).asUInt(),en,clk,scan_mode)
          else rvdffiee(din.getElements(i).asUInt(),clk,rst_l,en,scan_mode)))

        vec.asTypeOf(din)
      }
    }
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  object rvdffpcie {
    def apply(din: UInt, en: Bool, rst_l: AsyncReset, clk: Clock, scan_mode: Bool, WIDTH: Int = 31) = {
      if (RV_FPGA_OPTIMIZE) {
        withClock(clk) {
          RegEnable(din, 0.U.asTypeOf(din), en)
        }
      }
      else {
        rvdfflie_UInt(din, clk, rst_l, en, scan_mode, WIDTH, 19)
      }
    }
  }

  /////////////////////////////////////////////////////////
  def rvtwoscomp(din:UInt) = {   //Done for verification and testing
    val temp = Wire(Vec(din.getWidth-1,UInt(1.W)))
    for(i <- 1 to din.getWidth-1){
      temp(i-1) := Mux(din(i-1,0).orR ,~din(i),din(i))
    }
    Cat(temp.asUInt,din(0))
  }

  //implicit def bool2int(b:Boolean): Int = if (b) 1 else 0

}
