package lsu
import chisel3._
import chisel3.util._
import chisel3.experimental.chiselName
import include._
import lib._

@chiselName
class el2_lsu_ecc extends Module with el2_lib with RequireAsyncReset {
  val io = IO(new Bundle{

    val lsu_c2_r_clk        	     = Input(Clock())
    val lsu_pkt_m           	     = Input(new el2_lsu_pkt_t)
    val lsu_pkt_r           	     = Input(new el2_lsu_pkt_t)
    val stbuf_data_any	   	       = Input(UInt(DCCM_DATA_WIDTH.W))
    val dec_tlu_core_ecc_disable   = Input(Bool())
    val lsu_dccm_rden_r        	   = Input(Bool())
    val addr_in_dccm_r             = Input(Bool())

    val lsu_addr_r	   	           = Input(UInt(DCCM_BITS.W))
    val end_addr_r	   	           = Input(UInt(DCCM_BITS.W))
    val lsu_addr_m	   	           = Input(UInt(DCCM_BITS.W))
    val end_addr_m	   	           = Input(UInt(DCCM_BITS.W))

    val dccm_rdata_hi_r   	       = Input(UInt(DCCM_DATA_WIDTH.W))
    val dccm_rdata_lo_r   	       = Input(UInt(DCCM_DATA_WIDTH.W))
    val dccm_rdata_hi_m   	       = Input(UInt(DCCM_DATA_WIDTH.W))
    val dccm_rdata_lo_m   	       = Input(UInt(DCCM_DATA_WIDTH.W))

    val dccm_data_ecc_hi_r         = Input(UInt(DCCM_ECC_WIDTH.W))
    val dccm_data_ecc_lo_r         = Input(UInt(DCCM_ECC_WIDTH.W))
    val dccm_data_ecc_hi_m         = Input(UInt(DCCM_ECC_WIDTH.W))
    val dccm_data_ecc_lo_m         = Input(UInt(DCCM_ECC_WIDTH.W))

    val ld_single_ecc_error_r      = Input(Bool())
    val ld_single_ecc_error_r_ff   = Input(Bool())
    val lsu_dccm_rden_m            = Input(Bool())
    val addr_in_dccm_m             = Input(Bool())

    val dma_dccm_wen               = Input(Bool())
    val dma_dccm_wdata_lo          = Input(UInt(32.W))
    val dma_dccm_wdata_hi          = Input(UInt(32.W))

    val scan_mode                  = Input(Bool())

    //Outputs
    val sec_data_hi_r   	         = Output(UInt(DCCM_DATA_WIDTH.W))
    val sec_data_lo_r   	         = Output(UInt(DCCM_DATA_WIDTH.W))
    val sec_data_hi_m   	         = Output(UInt(DCCM_DATA_WIDTH.W))
    val sec_data_lo_m   	         = Output(UInt(DCCM_DATA_WIDTH.W))
    val sec_data_hi_r_ff   	       = Output(UInt(DCCM_DATA_WIDTH.W))
    val sec_data_lo_r_ff   	       = Output(UInt(DCCM_DATA_WIDTH.W))

    val dma_dccm_wdata_ecc_hi      = Output(UInt(DCCM_ECC_WIDTH.W))
    val dma_dccm_wdata_ecc_lo      = Output(UInt(DCCM_ECC_WIDTH.W))
    val stbuf_ecc_any              = Output(UInt(DCCM_ECC_WIDTH.W))
    val sec_data_ecc_hi_r_ff       = Output(UInt(DCCM_ECC_WIDTH.W))
    val sec_data_ecc_lo_r_ff       = Output(UInt(DCCM_ECC_WIDTH.W))

    val single_ecc_error_hi_r      = Output(Bool())
    val single_ecc_error_lo_r      = Output(Bool())
    val lsu_single_ecc_error_r     = Output(Bool())
    val lsu_double_ecc_error_r     = Output(Bool())
    val lsu_single_ecc_error_m     = Output(Bool())
    val lsu_double_ecc_error_m     = Output(Bool())
  })
  val is_ldst_r                    = WireInit(Bool(),init = 0.U)
  val is_ldst_hi_any               = WireInit(Bool(),init = 0.U)
  val is_ldst_lo_any               = WireInit(Bool(),init = 0.U)
  val dccm_wdata_hi_any            = WireInit(0.U(DCCM_DATA_WIDTH.W))
  val dccm_wdata_lo_any            = WireInit(0.U(DCCM_DATA_WIDTH.W))
  val dccm_rdata_hi_any            = WireInit(0.U(DCCM_DATA_WIDTH.W))
  val dccm_rdata_lo_any            = WireInit(0.U(DCCM_DATA_WIDTH.W))
  // val dccm_wdata_ecc_hi_any        = WireInit(0.U(DCCM_ECC_WIDTH.W))
  //val dccm_wdata_ecc_lo_any        = WireInit(0.U(DCCM_ECC_WIDTH.W))
  val dccm_data_ecc_hi_any         = WireInit(0.U(DCCM_ECC_WIDTH.W))
  val dccm_data_ecc_lo_any         = WireInit(0.U(DCCM_ECC_WIDTH.W))
  val double_ecc_error_hi_m	       = WireInit(Bool(),init = 0.U)
  val double_ecc_error_lo_m	       = WireInit(Bool(),init = 0.U)
  val double_ecc_error_hi_r	       = WireInit(Bool(),init = 0.U)
  val double_ecc_error_lo_r	       = WireInit(Bool(),init = 0.U)
  val ldst_dual_m	                 = WireInit(Bool(),init = 0.U)
  val ldst_dual_r 	               = WireInit(Bool(),init = 0.U)
  val is_ldst_m	                   = WireInit(Bool(),init = 0.U)
  val is_ldst_hi_m	               = WireInit(Bool(),init = 0.U)
  val is_ldst_lo_m	               = WireInit(Bool(),init = 0.U)
  val is_ldst_hi_r	               = WireInit(Bool(),init = 0.U)
  val is_ldst_lo_r	               = WireInit(Bool(),init = 0.U)

  io.sec_data_hi_m   	       :=0.U
  io.sec_data_lo_m   	       :=0.U
  io.lsu_single_ecc_error_m  :=0.U
  io.lsu_double_ecc_error_m  :=0.U

  //////////////////////////////CODE STARTS HERE///////////////////////
  val (ecc_out_hi_nc, sec_data_hi_any,  single_ecc_error_hi_any, double_ecc_error_hi_any) = if(DCCM_ENABLE)
    rvecc_decode(is_ldst_hi_any, dccm_rdata_hi_any, dccm_data_ecc_hi_any, 0.U) else (0.U, 0.U, 0.U, 0.U)
  val ( ecc_out_lo_nc, sec_data_lo_any, single_ecc_error_lo_any, double_ecc_error_lo_any) = if(DCCM_ENABLE)
    rvecc_decode(is_ldst_lo_any, dccm_rdata_lo_any, dccm_data_ecc_lo_any, 0.U) else (0.U, 0.U, 0.U, 0.U)
  val dccm_wdata_ecc_lo_any = if(DCCM_ENABLE) rvecc_encode(dccm_wdata_lo_any) else (0.U)
  val dccm_wdata_ecc_hi_any = if(DCCM_ENABLE) rvecc_encode(dccm_wdata_hi_any) else (0.U)

  when (LOAD_TO_USE_PLUS1.B) {
    ldst_dual_r := io.lsu_addr_r(2) =/= io.end_addr_r(2)
    is_ldst_r := io.lsu_pkt_r.valid & (io.lsu_pkt_r.load | io.lsu_pkt_r.store) & io.addr_in_dccm_r & io.lsu_dccm_rden_r
    is_ldst_lo_r := is_ldst_r & !io.dec_tlu_core_ecc_disable
    is_ldst_hi_r := is_ldst_r & (ldst_dual_r | io.lsu_pkt_r.dma) & !io.dec_tlu_core_ecc_disable
    is_ldst_hi_any  := is_ldst_hi_r
    dccm_rdata_hi_any  := io.dccm_rdata_hi_r
    dccm_data_ecc_hi_any := io.dccm_data_ecc_hi_r
    is_ldst_lo_any := is_ldst_lo_r
    dccm_rdata_lo_any   := io.dccm_rdata_lo_r
    dccm_data_ecc_lo_any := io.dccm_data_ecc_lo_r
    io.sec_data_hi_r := sec_data_hi_any;
    io.single_ecc_error_hi_r  := single_ecc_error_hi_any
    double_ecc_error_hi_r  := double_ecc_error_hi_any
    io.sec_data_lo_r    := sec_data_lo_any
    io.single_ecc_error_lo_r  := single_ecc_error_lo_any
    double_ecc_error_lo_r  := double_ecc_error_lo_any
    io.lsu_single_ecc_error_r := io.single_ecc_error_hi_r | io.single_ecc_error_lo_r;
    io.lsu_double_ecc_error_r := double_ecc_error_hi_r | double_ecc_error_lo_r
  }
    .otherwise {
      ldst_dual_m := io.lsu_addr_m(2) =/= io.end_addr_m(2)
      is_ldst_m := io.lsu_pkt_m.valid & (io.lsu_pkt_m.load | io.lsu_pkt_m.store) & io.addr_in_dccm_m & io.lsu_dccm_rden_m
      is_ldst_lo_m := is_ldst_m & !io.dec_tlu_core_ecc_disable
      is_ldst_hi_m := is_ldst_m & (ldst_dual_m | io.lsu_pkt_m.dma) & !io.dec_tlu_core_ecc_disable
      is_ldst_hi_any  := is_ldst_hi_m
      dccm_rdata_hi_any  := io.dccm_rdata_hi_m
      dccm_data_ecc_hi_any := io.dccm_data_ecc_hi_m
      is_ldst_lo_any := is_ldst_lo_m
      dccm_rdata_lo_any   := io.dccm_rdata_lo_m
      dccm_data_ecc_lo_any := io.dccm_data_ecc_lo_m
      io.sec_data_hi_m    := sec_data_hi_any
      double_ecc_error_hi_m  := double_ecc_error_hi_any
      io.sec_data_lo_m    := sec_data_lo_any
      double_ecc_error_lo_m  := double_ecc_error_lo_any
      io.lsu_single_ecc_error_m := single_ecc_error_hi_any | single_ecc_error_lo_any;
      io.lsu_double_ecc_error_m := double_ecc_error_hi_m | double_ecc_error_lo_m

      withClock(io.lsu_c2_r_clk) {io.lsu_single_ecc_error_r  := RegNext(io.lsu_single_ecc_error_m,0.U)}
      withClock(io.lsu_c2_r_clk) {io.lsu_double_ecc_error_r  := RegNext(io.lsu_double_ecc_error_m,0.U)}
      withClock(io.lsu_c2_r_clk) {io.single_ecc_error_lo_r   := RegNext(single_ecc_error_lo_any,0.U)}
      withClock(io.lsu_c2_r_clk) {io.single_ecc_error_hi_r   := RegNext(single_ecc_error_hi_any,0.U)}
      withClock(io.lsu_c2_r_clk) {io.sec_data_hi_r           := RegNext(io.sec_data_hi_m,0.U)}
      withClock(io.lsu_c2_r_clk) {io.sec_data_lo_r           := RegNext(io.sec_data_lo_m,0.U)}
    }
  // Logic for ECC generation during write
  dccm_wdata_lo_any := Mux(io.ld_single_ecc_error_r_ff.asBool, io.sec_data_lo_r_ff,Mux(io.dma_dccm_wen.asBool, io.dma_dccm_wdata_lo, io.stbuf_data_any))
  dccm_wdata_hi_any := Mux(io.ld_single_ecc_error_r_ff.asBool, io.sec_data_hi_r_ff,Mux(io.dma_dccm_wen.asBool, io.dma_dccm_wdata_hi, io.stbuf_data_any))
  io.sec_data_ecc_hi_r_ff  := dccm_wdata_ecc_hi_any
  io.sec_data_ecc_lo_r_ff  := dccm_wdata_ecc_lo_any
  io.stbuf_ecc_any         := dccm_wdata_ecc_lo_any
  io.dma_dccm_wdata_ecc_hi := dccm_wdata_ecc_hi_any
  io.dma_dccm_wdata_ecc_lo := dccm_wdata_ecc_lo_any

  io.sec_data_hi_r_ff := rvdffe(io.sec_data_hi_r, io.ld_single_ecc_error_r,clock,io.scan_mode)
  io.sec_data_lo_r_ff := rvdffe(io.sec_data_lo_r, io.ld_single_ecc_error_r,clock,io.scan_mode)

}
object eccmain extends App{
  println("Generate Verilog")
  println((new chisel3.stage.ChiselStage).emitVerilog(new el2_lsu_ecc()))
}
