import chisel3._
import chisel3.util._
import include._
import scala.collection._
import lib._

class dma_ctrl extends Module with lib with RequireAsyncReset {
  val io = IO(new Bundle {
    val free_clk              = Input(Clock())
    val dma_bus_clk_en        = Input(Bool())     // slave bus clock enable
    val clk_override          = Input(Bool())
    val scan_mode             = Input(Bool())
    val dbg_cmd_size          = Input(UInt(2.W))

    val dma_dbg_cmd_done      = Output(Bool())
    val dma_dbg_cmd_fail      = Output(Bool())
    val dma_dbg_rddata        = Output(UInt(32.W))
    val iccm_dma_rvalid       = Input(Bool())
    val iccm_dma_ecc_error    = Input(Bool())
    val iccm_dma_rtag         = Input(UInt(3.W))
    val iccm_dma_rdata        = Input(UInt(64.W))
    val dma_active            = Output(Bool())
    val iccm_ready            = Input(Bool())

    val dbg_dec_dma           = new dec_dbg()
    val dbg_dma               = new dbg_dma()
    val dec_dma               = Flipped(new dec_dma())
    val lsu_dma               = Flipped(new lsu_dma)
    val ifu_dma               = Flipped(new ifu_dma)// AXI Write Channel
    val dma_axi = Flipped(new axi_channels(DMA_BUS_TAG))

  })

  val DEPTH = DMA_BUF_DEPTH

  val DEPTH_PTR = log2Ceil(DEPTH)

  val NACK_COUNT = 7

  val      dma_dbg_mem_wrdata = WireInit(UInt(32.W), 0.U)
  val bus_cmd_addr = WireInit(UInt(32.W), 0.U)
  val bus_cmd_byteen = WireInit(UInt(8.W), 0.U)
  val bus_cmd_sz = WireInit(UInt(3.W), 0.U)
  val bus_cmd_write = WireInit(Bool(), false.B)
  val bus_cmd_posted_write = WireInit(Bool(), false.B)
  // Clock Gating logic
  val bus_cmd_valid = WireInit(Bool(),0.B)
  val bus_rsp_valid = WireInit(Bool(),0.B)
  val dma_dbg_cmd_done_q  = WireInit(Bool(),0.B)
  val fifo_valid  = WireInit(UInt(DEPTH.W),0.U)
  val dma_buffer_c1_clken = (bus_cmd_valid & io.dma_bus_clk_en) | io.dbg_dec_dma.dbg_ib.dbg_cmd_valid | io.clk_override
  val dma_free_clken = (bus_cmd_valid | bus_rsp_valid | io.dbg_dec_dma.dbg_ib.dbg_cmd_valid | io.dma_dbg_cmd_done | dma_dbg_cmd_done_q | (fifo_valid.orR) | io.clk_override)

  val dma_buffer_c1_clk = rvoclkhdr(clock,dma_buffer_c1_clken,io.scan_mode)
  val dma_free_clk = rvoclkhdr(clock,dma_free_clken,io.scan_mode)

  val fifo_addr_in = Mux(io.dbg_dec_dma.dbg_ib.dbg_cmd_valid, io.dbg_dec_dma.dbg_ib.dbg_cmd_addr, bus_cmd_addr)
  val fifo_byteen_in = Fill(8,!io.dbg_dec_dma.dbg_ib.dbg_cmd_valid) & bus_cmd_byteen    // Byte enable is used only for bus requests//Mux(io.dbg_cmd_valid, 0.U, bus_cmd_byteen)
  val fifo_sz_in = Mux(io.dbg_dec_dma.dbg_ib.dbg_cmd_valid, Cat(0.U,io.dbg_cmd_size), bus_cmd_sz)
  val fifo_write_in = Mux(io.dbg_dec_dma.dbg_ib.dbg_cmd_valid, io.dbg_dec_dma.dbg_ib.dbg_cmd_write, bus_cmd_write)
  val fifo_posted_write_in = !io.dbg_dec_dma.dbg_ib.dbg_cmd_valid & bus_cmd_posted_write
  val fifo_dbg_in = io.dbg_dec_dma.dbg_ib.dbg_cmd_valid
  val bus_cmd_sent = WireInit(Bool(), false.B)
  val WrPtr = WireInit(UInt(DEPTH_PTR.W), 0.U)
  val RdPtr = WireInit(UInt(DEPTH_PTR.W), 0.U)
  val dma_address_error = WireInit(Bool(), false.B)
  val dma_alignment_error = WireInit(Bool(), false.B)

  val fifo_cmd_en = WireInit(UInt(DMA_BUF_DEPTH.W), 0.U)

  val fifo_data_en = WireInit(UInt(DMA_BUF_DEPTH.W), 0.U)

  val fifo_pend_en = WireInit(UInt(DMA_BUF_DEPTH.W), 0.U)

  val fifo_error_bus_en = WireInit(UInt(DMA_BUF_DEPTH.W), 0.U)

  val fifo_done_en = WireInit(UInt(DMA_BUF_DEPTH.W), 0.U)

  val fifo_done_bus_en = WireInit(UInt(DMA_BUF_DEPTH.W), 0.U)

  val fifo_reset = WireInit(UInt(DMA_BUF_DEPTH.W), 0.U)

  val fifo_error_en = WireInit(UInt(DMA_BUF_DEPTH.W),0.U)
  val dma_dbg_cmd_error = WireInit(UInt(1.W),0.U)
  val fifo_error_in = Wire(Vec(DMA_BUF_DEPTH, UInt(2.W)))
  val RspPtr = WireInit(UInt((log2Ceil(DMA_BUF_DEPTH)).W), 0.U)
  val bus_posted_write_done = WireInit(UInt(1.W), 0.U)
  val bus_rsp_sent = WireInit(UInt(1.W), 0.U)
  val fifo_error = Wire(Vec(DMA_BUF_DEPTH, UInt(2.W)))
  val fifo_done = WireInit(UInt(DMA_BUF_DEPTH.W), 0.U)
  fifo_cmd_en       := (0 until DMA_BUF_DEPTH).map(i => (((bus_cmd_sent.asBool & io.dma_bus_clk_en) | (io.dbg_dec_dma.dbg_ib.dbg_cmd_valid & io.dbg_dec_dma.dbg_ib.dbg_cmd_type(1).asBool)) & (i.U === WrPtr)).asUInt).reverse.reduce(Cat(_,_))

  fifo_data_en      := (0 until DMA_BUF_DEPTH).map(i => (((bus_cmd_sent & fifo_write_in & io.dma_bus_clk_en) | (io.dbg_dec_dma.dbg_ib.dbg_cmd_valid & io.dbg_dec_dma.dbg_ib.dbg_cmd_type(1) & io.dbg_dec_dma.dbg_ib.dbg_cmd_write)) & (i.U === WrPtr).asUInt()) | ((dma_address_error | dma_alignment_error) & (i.U === RdPtr).asUInt()) | (io.lsu_dma.dma_dccm_ctl.dccm_dma_rvalid & (i.U === io.lsu_dma.dma_dccm_ctl.dccm_dma_rtag).asUInt()) | (io.iccm_dma_rvalid & (i.U === io.iccm_dma_rtag).asUInt())).reverse.reduce(Cat(_,_))

  fifo_pend_en      := (0 until DMA_BUF_DEPTH).map(i => ((io.lsu_dma.dma_lsc_ctl.dma_dccm_req | io.ifu_dma.dma_mem_ctl.dma_iccm_req) & !io.lsu_dma.dma_lsc_ctl.dma_mem_write & (i.U === RdPtr)).asUInt).reverse.reduce(Cat(_,_))

  fifo_error_en     := (0 until DMA_BUF_DEPTH).map(i => (((dma_address_error.asBool | dma_alignment_error.asBool | dma_dbg_cmd_error) & (i.U === RdPtr)) | ((io.lsu_dma.dma_dccm_ctl.dccm_dma_rvalid & io.lsu_dma.dma_dccm_ctl.dccm_dma_ecc_error) & (i.U === io.lsu_dma.dma_dccm_ctl.dccm_dma_rtag)) | ((io.iccm_dma_rvalid & io.iccm_dma_ecc_error) & (i.U === io.iccm_dma_rtag))).asUInt).reverse.reduce(Cat(_,_))

  fifo_error_bus_en := (0 until DMA_BUF_DEPTH).map(i => ((((fifo_error_in(i)(1,0).orR) & fifo_error_en(i)) | (fifo_error(i).orR)) & io.dma_bus_clk_en).asUInt).reverse.reduce(Cat(_,_))

  fifo_done_en      := (0 until DMA_BUF_DEPTH).map(i => (((fifo_error(i).orR | fifo_error_en(i) | ((io.lsu_dma.dma_lsc_ctl.dma_dccm_req | io.ifu_dma.dma_mem_ctl.dma_iccm_req) & io.lsu_dma.dma_lsc_ctl.dma_mem_write)) & (i.U === RdPtr)) | (io.lsu_dma.dma_dccm_ctl.dccm_dma_rvalid & (i.U === io.lsu_dma.dma_dccm_ctl.dccm_dma_rtag)) | (io.iccm_dma_rvalid & (i.U === io.iccm_dma_rtag))).asUInt).reverse.reduce(Cat(_,_))

  fifo_done_bus_en  := (0 until DMA_BUF_DEPTH).map(i => ((fifo_done_en(i) | fifo_done(i)) & io.dma_bus_clk_en).asUInt).reverse.reduce(Cat(_,_))

  fifo_reset        := (0 until DMA_BUF_DEPTH).map(i => ((((bus_rsp_sent | bus_posted_write_done) & io.dma_bus_clk_en) | io.dma_dbg_cmd_done) & (i.U === RspPtr))).reverse.reduce(Cat(_,_))

  (0 until DMA_BUF_DEPTH).map(i => fifo_error_in(i) := (Mux(io.lsu_dma.dma_dccm_ctl.dccm_dma_rvalid & (i.U === io.lsu_dma.dma_dccm_ctl.dccm_dma_rtag), Cat(0.U, io.lsu_dma.dma_dccm_ctl.dccm_dma_ecc_error), Mux(io.iccm_dma_rvalid & (i.U === io.iccm_dma_rtag), (Cat(0.U, io.iccm_dma_ecc_error)), (Cat((dma_address_error | dma_alignment_error | dma_dbg_cmd_error), dma_alignment_error))))))

  val fifo_addr = Wire(Vec(DEPTH,UInt(32.W)))
  val bus_cmd_wdata = WireInit(UInt(64.W), 0.U)
  val fifo_data_in = VecInit.tabulate(DMA_BUF_DEPTH)(i =>(Mux(fifo_error_en(i) & (fifo_error_in(i).orR), Cat(Fill(32, 0.U), fifo_addr(i)), Mux(io.lsu_dma.dma_dccm_ctl.dccm_dma_rvalid & (i.U === io.lsu_dma.dma_dccm_ctl.dccm_dma_rtag), io.lsu_dma.dma_dccm_ctl.dccm_dma_rdata, Mux(io.iccm_dma_rvalid & (i.U === io.iccm_dma_rtag), io.iccm_dma_rdata, Mux(io.dbg_dec_dma.dbg_ib.dbg_cmd_valid, Fill(2, dma_dbg_mem_wrdata), bus_cmd_wdata(63,0)))))))

  fifo_valid := (0 until DMA_BUF_DEPTH).map(i => withClock(dma_free_clk) {RegNext(Mux(fifo_cmd_en(i), 1.U, fifo_valid(i)) & !fifo_reset(i), 0.U)}).reverse.reduce(Cat(_,_))
  (0 until DMA_BUF_DEPTH).map(i => fifo_error(i) := withClock(dma_free_clk) {RegNext(Mux(fifo_error_en(i).asBool(),fifo_error_in(i) , fifo_error(i)) & Fill(fifo_error_in(i).getWidth , !fifo_reset(i)), 0.U)})
  val fifo_error_bus = WireInit(UInt(DEPTH.W), 0.U)
  val fifo_rpend     = WireInit(UInt(DEPTH.W), 0.U)

  fifo_rpend       := (0 until DMA_BUF_DEPTH).map(i => withClock(dma_free_clk) {RegNext(Mux(fifo_pend_en(i), 1.U, fifo_rpend(i)) & !fifo_reset(i), 0.U)}).reverse.reduce(Cat(_,_))
  fifo_done        := (0 until DMA_BUF_DEPTH).map(i => withClock(dma_free_clk) {RegNext(Mux(fifo_done_en(i), 1.U, fifo_done(i)) & !fifo_reset(i), 0.U)}).reverse.reduce(Cat(_,_))
  val fifo_done_bus = WireInit(UInt(DEPTH.W), 0.U)
  fifo_done_bus    := (0 until DMA_BUF_DEPTH).map(i => withClock(dma_free_clk) {RegNext(Mux(fifo_done_bus_en(i), 1.U, fifo_done_bus(i)) & !fifo_reset(i), 0.U)}).reverse.reduce(Cat(_,_))
  (0 until DMA_BUF_DEPTH).map(i => fifo_addr(i) := rvdffe(fifo_addr_in, fifo_cmd_en(i), clock, io.scan_mode))
  val fifo_sz           = VecInit.tabulate(DMA_BUF_DEPTH)(i => withClock(dma_buffer_c1_clk) {RegEnable(fifo_sz_in(2,0), 0.U, fifo_cmd_en(i))})
  val fifo_byteen       = VecInit.tabulate(DMA_BUF_DEPTH)(i =>withClock(dma_buffer_c1_clk) {RegEnable(fifo_byteen_in(7,0), 0.U, fifo_cmd_en(i).asBool())})
  val fifo_write        = (0 until DMA_BUF_DEPTH).map(i => (withClock(dma_buffer_c1_clk) {RegEnable(fifo_write_in, 0.U, fifo_cmd_en(i))})).reverse.reduce(Cat(_,_))
  val fifo_posted_write = (0 until DMA_BUF_DEPTH).map(i => (withClock(dma_buffer_c1_clk) {RegEnable(fifo_posted_write_in, 0.U, fifo_cmd_en(i))})).reverse.reduce(Cat(_,_))
  val fifo_dbg          = (0 until DMA_BUF_DEPTH).map(i => withClock(dma_buffer_c1_clk) {RegEnable(fifo_dbg_in, 0.U, fifo_cmd_en(i))}).reverse.reduce(Cat(_,_))

  val fifo_data = Wire(Vec(DMA_BUF_DEPTH,UInt(64.W)))
  (0 until DMA_BUF_DEPTH).map(i => fifo_data(i) := rvdffe(fifo_data_in(i), fifo_data_en(i), clock, io.scan_mode))
  val bus_cmd_tag = WireInit(UInt(DMA_BUS_TAG.W),0.U)
  val bus_cmd_mid = WireInit(UInt(DMA_BUS_ID.W),0.U)
  val bus_cmd_prty = WireInit(UInt(DMA_BUS_PRTY.W),0.U)
  val fifo_tag      = VecInit.tabulate(DMA_BUF_DEPTH)(i =>withClock(dma_buffer_c1_clk) {RegEnable(bus_cmd_tag, 0.U, fifo_cmd_en(i))})
  val fifo_mid      = VecInit.tabulate(DMA_BUF_DEPTH)(i =>withClock(dma_buffer_c1_clk) {RegEnable(bus_cmd_mid, 0.U, fifo_cmd_en(i))})
  val fifo_prty     = VecInit.tabulate(DMA_BUF_DEPTH)(i =>withClock(dma_buffer_c1_clk) {RegEnable(bus_cmd_prty, 0.U, fifo_cmd_en(i))})

  // Pointer logic

  val NxtWrPtr  = Mux((WrPtr  === (DEPTH-1).U), 0.U, WrPtr+ 1.U)
  val NxtRdPtr  = Mux((RdPtr  === (DEPTH-1).U), 0.U, RdPtr+ 1.U)
  val NxtRspPtr = Mux((RspPtr === (DEPTH-1).U), 0.U, RspPtr + 1.U)

  val WrPtrEn = fifo_cmd_en.orR
  val RdPtrEn = io.lsu_dma.dma_lsc_ctl.dma_dccm_req | io.ifu_dma.dma_mem_ctl.dma_iccm_req | (dma_address_error | dma_alignment_error | dma_dbg_cmd_error)
  val RspPtrEn = (io.dma_dbg_cmd_done | (bus_rsp_sent | bus_posted_write_done) & io.dma_bus_clk_en)

  WrPtr        := withClock(dma_free_clk) { RegEnable(NxtWrPtr, 0.U, WrPtrEn) }
  RdPtr        := withClock(dma_free_clk) { RegEnable(NxtRdPtr, 0.U, RdPtrEn.asBool) }
  RspPtr       := withClock(dma_free_clk) { RegEnable(NxtRspPtr, 0.U, RspPtrEn.asBool) }
  // Miscellaneous signals
  val fifo_full_spec_bus = WireInit(Bool(),0.B)
  val fifo_full = fifo_full_spec_bus

  val num_fifo_vld = Wire(Vec(DEPTH+1,UInt(4.W)))
  val dbg_dma_bubble_bus = WireInit(Bool(),0.B)
  num_fifo_vld(0) := Cat(0.U(3.W),bus_cmd_sent) - Cat(0.U(3.W),bus_rsp_sent)
  for (i <- 1 to DEPTH) { num_fifo_vld(i):= num_fifo_vld(i-1) + Cat(0.U(3.W),fifo_valid(i-1))}
  val fifo_full_spec      = (num_fifo_vld(DEPTH) >= DEPTH.U)
  val dma_fifo_ready = ~(fifo_full | dbg_dma_bubble_bus)

  // Error logic
  val dma_mem_addr_in_dccm = WireInit(Bool(),0.B)
  val dma_mem_addr_in_iccm = WireInit(Bool(),0.B)
  val dma_mem_sz_int = WireInit(UInt(3.W),0.U)
  val dma_mem_addr_int = WireInit(UInt(32.W),0.U)
  val dma_mem_byteen = WireInit(UInt(8.W),0.U)
  dma_address_error  := fifo_valid(RdPtr) & ~fifo_done(RdPtr) & ~fifo_dbg(RdPtr) & (~(dma_mem_addr_in_dccm | dma_mem_addr_in_iccm))    // request not for ICCM or DCCM

  dma_alignment_error   := fifo_valid(RdPtr) & !fifo_done(RdPtr) & !fifo_dbg(RdPtr)  & !dma_address_error &
    (((dma_mem_sz_int(2,0) === 1.U) & dma_mem_addr_int(0)) |                                                                                // HW size but unaligned
      ((dma_mem_sz_int(2,0) === 2.U) & (dma_mem_addr_int(1, 0).orR)) |                                                                      // W size but unaligned
      ((dma_mem_sz_int(2,0) === 3.U) & (dma_mem_addr_int(2, 0).orR)) |                                                                      // DW size but unaligned
      (dma_mem_addr_in_iccm & ~((dma_mem_sz_int(1, 0) === 2.U) | (dma_mem_sz_int(1, 0) === 3.U)).asUInt  ) |                                // ICCM access not word size
      (dma_mem_addr_in_dccm & io.lsu_dma.dma_lsc_ctl.dma_mem_write & ~((dma_mem_sz_int(1, 0) === 2.U) | (dma_mem_sz_int(1, 0) === 3.U)).asUInt) |               // DCCM write not word size
      (io.lsu_dma.dma_lsc_ctl.dma_mem_write & (dma_mem_sz_int(2, 0) === 2.U) & (Mux1H(Seq((dma_mem_addr_int(2,0) === 0.U) -> (dma_mem_byteen(3,0)),
        (dma_mem_addr_int(2,0) === 1.U) -> (dma_mem_byteen(4,1)),
        (dma_mem_addr_int(2,0) === 2.U) -> (dma_mem_byteen(5,2)),
        (dma_mem_addr_int(2,0) === 3.U) -> (dma_mem_byteen(6,3)),
        (dma_mem_addr_int(2,0) === 4.U) -> (dma_mem_byteen(7,4)),
        (dma_mem_addr_int(2,0) === 5.U) -> (dma_mem_byteen(7,5)),
        (dma_mem_addr_int(2,0) === 6.U) -> (dma_mem_byteen(7,6)),
        (dma_mem_addr_int(2,0) === 7.U) -> (dma_mem_byteen(7)))) =/= "hf".U)) | // Write byte enables not aligned for word store
      (io.lsu_dma.dma_lsc_ctl.dma_mem_write & (dma_mem_sz_int(2, 0) === 3.U) & !((dma_mem_byteen(7,0) === "h0f".U) | (dma_mem_byteen(7,0) === "hf0".U) | (dma_mem_byteen(7,0) === "hff".U)))) // Write byte enables not aligned for dword store
  // Used to indicate ready to debug
  val fifo_empty     = ~(fifo_valid.orR | bus_cmd_sent)

  //Dbg outputs
  io.dbg_dma.dma_dbg_ready    := fifo_empty & io.dbg_dma.dbg_dma_bubble
  io.dma_dbg_cmd_done := (fifo_valid(RspPtr) & fifo_dbg(RspPtr) & fifo_done(RspPtr))
  io.dma_dbg_cmd_fail     := fifo_error(RspPtr).orR

  val dma_dbg_sz          = fifo_sz(RspPtr)(1,0)
  val dma_dbg_addr        = fifo_addr(RspPtr)(1,0)
  val dma_dbg_mem_rddata  = Mux(fifo_addr(RspPtr)(2), fifo_data(RspPtr)(63,32) , fifo_data(RspPtr)(31,0))
  io.dma_dbg_rddata      := Mux1H(Seq(
    (dma_dbg_sz(1,0) === "h0".U(2.W)) -> ((dma_dbg_mem_rddata >> ((8.U)*dma_dbg_addr(1,0))) & "hff".U) ,
    (dma_dbg_sz(1,0) === "h1".U(2.W)) -> ((dma_dbg_mem_rddata >> ((16.U)*dma_dbg_addr(1))) & "hffff".U) ,
    (dma_dbg_sz(1,0) === "h2".U(2.W)) -> dma_dbg_mem_rddata))

  // PIC memory address check

  val dma_mem_addr_in_pic           = rvrangecheck_ch(dma_mem_addr_int(31,0),aslong(PIC_BASE_ADDR).U,PIC_SIZE)._1
  val dma_mem_addr_in_pic_region_nc = rvrangecheck_ch(dma_mem_addr_int(31,0),aslong(PIC_BASE_ADDR).U,PIC_SIZE)._2

  dma_dbg_cmd_error := fifo_valid(RdPtr) & ~fifo_done(RdPtr) & fifo_dbg(RdPtr) &
    ((~(dma_mem_addr_in_dccm | dma_mem_addr_in_iccm | dma_mem_addr_in_pic)) |             // Address outside of ICCM/DCCM/PIC
      ((dma_mem_addr_in_iccm | dma_mem_addr_in_pic) & (dma_mem_sz_int(1,0) =/= 2.U)))    // Only word accesses allowed for ICCM/PIC

  dma_dbg_mem_wrdata := Mux1H(Seq(
    (io.dbg_cmd_size(1,0) === "h0".U(2.W)) -> Fill(4,io.dbg_dec_dma.dbg_dctl.dbg_cmd_wrdata(7,0)) ,
    (io.dbg_cmd_size(1,0) === "h1".U(2.W)) -> Fill(2,io.dbg_dec_dma.dbg_dctl.dbg_cmd_wrdata(15,0)),
    (io.dbg_cmd_size(1,0) === "h2".U(2.W)) -> io.dbg_dec_dma.dbg_dctl.dbg_cmd_wrdata ))

  // Block the decode if fifo full
  val dma_mem_req        = WireInit(Bool(),0.B)
  val dma_nack_count     = WireInit(UInt(3.W),0.U)
  val dma_nack_count_csr = WireInit(UInt(3.W),0.U)
  val dma_nack_count_d   = WireInit(UInt(3.W),0.U)
  io.dec_dma.dctl_dma.dma_dccm_stall_any := dma_mem_req & (dma_mem_addr_in_dccm | dma_mem_addr_in_pic) & (dma_nack_count >= dma_nack_count_csr)
  io.dec_dma.tlu_dma.dma_dccm_stall_any := io.dec_dma.dctl_dma.dma_dccm_stall_any
  io.dec_dma.tlu_dma.dma_iccm_stall_any := dma_mem_req & dma_mem_addr_in_iccm & (dma_nack_count >= dma_nack_count_csr)
  io.ifu_dma.dma_ifc.dma_iccm_stall_any :=  io.dec_dma.tlu_dma.dma_iccm_stall_any
  // Nack counter, stall the lsu pipe if 7 nacks
  dma_nack_count_csr := io.dec_dma.tlu_dma.dec_tlu_dma_qos_prty
  dma_nack_count_d   := Mux((dma_nack_count >= dma_nack_count_csr), (Fill(3,(!(io.lsu_dma.dma_lsc_ctl.dma_dccm_req | io.ifu_dma.dma_mem_ctl.dma_iccm_req))) & dma_nack_count),
    Mux((dma_mem_req & ~(io.lsu_dma.dma_lsc_ctl.dma_dccm_req | io.ifu_dma.dma_mem_ctl.dma_iccm_req)), (dma_nack_count + 1.U), 0.U(3.W)))
  dma_nack_count := withClock(dma_free_clk){RegEnable(dma_nack_count_d,0.U,dma_mem_req)}

  // Core outputs
  dma_mem_req          := fifo_valid(RdPtr) & ~fifo_rpend(RdPtr) & ~fifo_done(RdPtr) & ~(dma_address_error | dma_alignment_error | dma_dbg_cmd_error)
  io.lsu_dma.dma_lsc_ctl.dma_dccm_req      := dma_mem_req & (dma_mem_addr_in_dccm | dma_mem_addr_in_pic) & io.lsu_dma.dccm_ready
  io.ifu_dma.dma_mem_ctl.dma_iccm_req      := dma_mem_req & dma_mem_addr_in_iccm & io.iccm_ready
  io.lsu_dma.dma_mem_tag       := RdPtr
  io.ifu_dma.dma_mem_ctl.dma_mem_tag :=    io.lsu_dma.dma_mem_tag
  dma_mem_addr_int     := fifo_addr(RdPtr)
  dma_mem_sz_int       := fifo_sz(RdPtr)
  io.lsu_dma.dma_dccm_ctl.dma_mem_addr      := Mux((io.lsu_dma.dma_lsc_ctl.dma_mem_write & ~fifo_dbg(RdPtr) & (dma_mem_byteen === "hf0".U(8.W))), Cat(dma_mem_addr_int(31,3),1.U,dma_mem_addr_int(1,0)), dma_mem_addr_int)
  io.lsu_dma.dma_lsc_ctl.dma_mem_addr :=  io.lsu_dma.dma_dccm_ctl.dma_mem_addr
  io.ifu_dma.dma_mem_ctl.dma_mem_addr :=   io.lsu_dma.dma_dccm_ctl.dma_mem_addr
  io.lsu_dma.dma_lsc_ctl.dma_mem_sz        := Mux(io.lsu_dma.dma_lsc_ctl.dma_mem_write & ~fifo_dbg(RdPtr) & ((dma_mem_byteen === "h0f".U(8.W)) | (dma_mem_byteen === "hf0".U(8.W))), 2.U(3.W), dma_mem_sz_int)
  io.ifu_dma.dma_mem_ctl.dma_mem_sz :=   io.lsu_dma.dma_lsc_ctl.dma_mem_sz
  dma_mem_byteen       := fifo_byteen(RdPtr)
  io.lsu_dma.dma_lsc_ctl.dma_mem_write     := fifo_write(RdPtr)
  io.ifu_dma.dma_mem_ctl.dma_mem_write :=  io.lsu_dma.dma_lsc_ctl.dma_mem_write
  io.lsu_dma.dma_dccm_ctl.dma_mem_wdata     := fifo_data(RdPtr)
  io.lsu_dma.dma_lsc_ctl.dma_mem_wdata :=  io.lsu_dma.dma_dccm_ctl.dma_mem_wdata
  io.ifu_dma.dma_mem_ctl.dma_mem_wdata :=  io.lsu_dma.dma_dccm_ctl.dma_mem_wdata

  // PMU outputs
  io.dec_dma.tlu_dma.dma_pmu_dccm_read   := io.lsu_dma.dma_lsc_ctl.dma_dccm_req & ~io.lsu_dma.dma_lsc_ctl.dma_mem_write
  io.dec_dma.tlu_dma.dma_pmu_dccm_write  := io.lsu_dma.dma_lsc_ctl.dma_dccm_req & io.lsu_dma.dma_lsc_ctl.dma_mem_write
  io.dec_dma.tlu_dma.dma_pmu_any_read    := (io.lsu_dma.dma_lsc_ctl.dma_dccm_req | io.ifu_dma.dma_mem_ctl.dma_iccm_req) & ~io.lsu_dma.dma_lsc_ctl.dma_mem_write
  io.dec_dma.tlu_dma.dma_pmu_any_write   := (io.lsu_dma.dma_lsc_ctl.dma_dccm_req | io.ifu_dma.dma_mem_ctl.dma_iccm_req) & io.lsu_dma.dma_lsc_ctl.dma_mem_write

  // Address check  dccm
  val dma_mem_addr_in_dccm_region_nc     = WireInit(Bool(),0.B)
  if (DCCM_ENABLE){
    dma_mem_addr_in_dccm           := rvrangecheck_ch(dma_mem_addr_int(31,0),aslong(DCCM_SADR).U,DCCM_SIZE)._1
    dma_mem_addr_in_dccm_region_nc := rvrangecheck_ch(dma_mem_addr_int(31,0),aslong(DCCM_SADR).U,DCCM_SIZE)._2
  } else{
    dma_mem_addr_in_dccm := 0.U
    dma_mem_addr_in_dccm_region_nc := 0.U
  }

  // Address check  iccm
  val dma_mem_addr_in_iccm_region_nc     = WireInit(Bool(),0.B)
  if (ICCM_ENABLE) {
    dma_mem_addr_in_iccm           := rvrangecheck_ch(dma_mem_addr_int(31,0),aslong(ICCM_SADR).U,ICCM_SIZE)._1
    dma_mem_addr_in_iccm_region_nc := rvrangecheck_ch(dma_mem_addr_int(31,0),aslong(ICCM_SADR).U,ICCM_SIZE)._2

  }else {
    dma_mem_addr_in_iccm := 0.U
    dma_mem_addr_in_iccm_region_nc := 0.U
  }

  val dma_bus_clk = Wire(Clock())
  if(RV_FPGA_OPTIMIZE) dma_bus_clk := 0.B.asClock()
  else  dma_bus_clk := rvclkhdr(clock,io.dma_bus_clk_en,io.scan_mode)//  dma_bus_cgc (.en(dma_bus_clk_en), .l1clk(dma_bus_clk), .*)

  // Inputs
  fifo_full_spec_bus := rvdff_fpga(fifo_full_spec,dma_bus_clk,io.dma_bus_clk_en,clock)
  dbg_dma_bubble_bus := rvdff_fpga(io.dbg_dma.dbg_dma_bubble,dma_bus_clk,io.dma_bus_clk_en,clock)
  dma_dbg_cmd_done_q := withClock(io.free_clk){ RegNext(io.dma_dbg_cmd_done,0.U)}

  // Write channel buffer
  val wrbuf_en       = io.dma_axi.aw.valid & io.dma_axi.aw.ready
  val wrbuf_data_en  = io.dma_axi.w.valid & io.dma_axi.w.ready
  val wrbuf_cmd_sent = bus_cmd_sent & bus_cmd_write
  val wrbuf_rst      = wrbuf_cmd_sent & ~wrbuf_en
  val wrbuf_data_rst = wrbuf_cmd_sent & ~wrbuf_data_en

  val wrbuf_vld      = rvdffsc_fpga(1.B,wrbuf_en,wrbuf_rst,dma_bus_clk,io.dma_bus_clk_en,clock)
  val wrbuf_data_vld = rvdffsc_fpga(1.B,wrbuf_data_en,wrbuf_data_rst,dma_bus_clk,io.dma_bus_clk_en,clock)
  val wrbuf_tag = rvdffs_fpga(io.dma_axi.aw.bits.id,wrbuf_en,dma_bus_clk,io.dma_bus_clk_en,clock)
  val wrbuf_sz = rvdffs_fpga(io.dma_axi.aw.bits.size,wrbuf_en,dma_bus_clk,io.dma_bus_clk_en,clock)
  val wrbuf_addr = rvdffe(io.dma_axi.aw.bits.addr,wrbuf_en & io.dma_bus_clk_en,clock,io.scan_mode)
  val wrbuf_data = rvdffe(io.dma_axi.w.bits.data,wrbuf_data_en & io.dma_bus_clk_en,clock,io.scan_mode)
  val wrbuf_byteen = rvdffs_fpga(io.dma_axi.w.bits.strb,wrbuf_data_en,dma_bus_clk,io.dma_bus_clk_en,clock)

  // Read channel buffer
  val rdbuf_en    = io.dma_axi.ar.valid & io.dma_axi.ar.ready
  val rdbuf_cmd_sent = bus_cmd_sent & ~bus_cmd_write
  val rdbuf_rst   = rdbuf_cmd_sent & ~rdbuf_en

  val rdbuf_vld  = rvdffsc_fpga(1.B,rdbuf_en,rdbuf_rst,dma_bus_clk,io.dma_bus_clk_en,clock)
  val rdbuf_tag  = rvdffs_fpga(io.dma_axi.ar.bits.id,rdbuf_en,dma_bus_clk,io.dma_bus_clk_en,clock)
  val rdbuf_sz   = rvdffs_fpga(io.dma_axi.ar.bits.size,rdbuf_en,dma_bus_clk,io.dma_bus_clk_en,clock)
  val rdbuf_addr = rvdffe(io.dma_axi.ar.bits.addr,rdbuf_en & io.dma_bus_clk_en,clock,io.scan_mode)

  io.dma_axi.aw.ready := ~(wrbuf_vld & ~wrbuf_cmd_sent)
  io.dma_axi.w.ready  := ~(wrbuf_data_vld & ~wrbuf_cmd_sent)
  io.dma_axi.ar.ready := ~(rdbuf_vld & ~rdbuf_cmd_sent)

  //Generate a single request from read/write channel
  val axi_mstr_sel = WireInit(Bool(),0.B)
  bus_cmd_valid               := (wrbuf_vld & wrbuf_data_vld) | rdbuf_vld
  bus_cmd_sent                := bus_cmd_valid & dma_fifo_ready
  bus_cmd_write               := axi_mstr_sel
  bus_cmd_posted_write        := 0.U
  bus_cmd_addr                := Mux(axi_mstr_sel, wrbuf_addr, rdbuf_addr)
  bus_cmd_sz                  := Mux(axi_mstr_sel, wrbuf_sz, rdbuf_sz)
  bus_cmd_wdata               := wrbuf_data
  bus_cmd_byteen              := wrbuf_byteen
  bus_cmd_tag                 := Mux(axi_mstr_sel, wrbuf_tag, rdbuf_tag)
  bus_cmd_mid                 := 0.U
  bus_cmd_prty                := 0.U

  // Sel=1 -> write has higher priority
  val axi_mstr_priority = WireInit(Bool(),0.B)
  axi_mstr_sel     := Mux(((wrbuf_vld & wrbuf_data_vld & rdbuf_vld)===1.U).asBool(), axi_mstr_priority, (wrbuf_vld & wrbuf_data_vld) )
  val axi_mstr_prty_in = ~axi_mstr_priority
  val axi_mstr_prty_en = bus_cmd_sent
  axi_mstr_priority   := rvdffs_fpga(axi_mstr_prty_in.asUInt(),axi_mstr_prty_en,dma_bus_clk,io.dma_bus_clk_en,clock)

  val axi_rsp_valid        = fifo_valid(RspPtr) & ~fifo_dbg(RspPtr) & fifo_done_bus(RspPtr)
  val axi_rsp_rdata        = fifo_data(RspPtr)
  val axi_rsp_write        = fifo_write(RspPtr)
  val axi_rsp_error        = Mux(fifo_error(RspPtr)(0), 2.U,Mux(fifo_error(RspPtr)(1), 3.U, 0.U))
  val axi_rsp_tag          = fifo_tag(RspPtr)

  // AXI response channel signals
  io.dma_axi.b.valid        := axi_rsp_valid & axi_rsp_write
  io.dma_axi.b.bits.resp         := axi_rsp_error
  io.dma_axi.b.bits.id           := axi_rsp_tag

  io.dma_axi.r.valid        := axi_rsp_valid & ~axi_rsp_write
  io.dma_axi.r.bits.resp         := axi_rsp_error
  io.dma_axi.r.bits.data         := axi_rsp_rdata
  io.dma_axi.r.bits.last         := 1.U
  io.dma_axi.r.bits.id           := axi_rsp_tag

  bus_posted_write_done := 0.U
  bus_rsp_valid      := (io.dma_axi.b.valid | io.dma_axi.r.valid)
  bus_rsp_sent       := (io.dma_axi.b.valid & io.dma_axi.b.ready) | (io.dma_axi.r.valid & io.dma_axi.r.ready)

  io.dma_active  := wrbuf_vld | rdbuf_vld | (fifo_valid.orR)

}
object DMA extends App {
  println((new chisel3.stage.ChiselStage).emitVerilog(new dma_ctrl))
}

fifo_error_bus   := (0 until DMA_BUF_DEPTH).map(i => withClock(dma_free_clk) {RegNext(Mux(fifo_error_bus_en(i), 1.U, fifo_error_bus(i)) & !fifo_reset(i), 0.U)}).reverse.reduce(Cat(_,_))


package dmi
import chisel3._
import chisel3.util._
import lib._

class dmi_wrapper extends BlackBox with HasBlackBoxResource{
  val io = IO(new Bundle{
    val trst_n         = Input(Bool())
    val tck            = Input(Clock())
    val tms            = Input(UInt(1.W))
    val tdi            = Input(UInt(1.W))
    val tdo            = Output(UInt(1.W))
    val tdoEnable      = Output(UInt(1.W))
    val core_rst_n     = Input(AsyncReset())
    val core_clk       = Input(Clock())
    val jtag_id        = Input(UInt(31.W))
    val rd_data        = Input(UInt(32.W))
    val reg_wr_data    = Output(UInt(32.W))
    val reg_wr_addr    = Output(UInt(7.W))
    val reg_en         = Output(UInt(1.W))
    val reg_wr_en      = Output(UInt(1.W))
    val dmi_hard_reset = Output(UInt(1.W))
  })
  addResource("/vsrc/dmi_wrapper.sv")
}
class dmi_wrapper_module extends Module{
  val io = IO(new Bundle{
    val trst_n         = Input(Bool())
    val tck            = Input(Clock())
    val tms            = Input(UInt(1.W))
    val tdi            = Input(UInt(1.W))
    val tdo            = Output(UInt(1.W))
    val tdoEnable      = Output(UInt(1.W))
    val core_rst_n     = Input(AsyncReset())
    val core_clk       = Input(Clock())
    val jtag_id        = Input(UInt(32.W))
    val rd_data        = Input(UInt(32.W))
    val reg_wr_data    = Output(UInt(32.W))

  //addResource("/vsrc/dmi_wrapper.v")
  val dwrap = Module(new dmi_wrapper)
  dwrap.io <> io
}


package exu
import chisel3._

import scala.collection._
import chisel3.util._
import include._
import lib._
import chisel3.experimental.chiselName

@chiselName
class exu extends Module with lib with RequireAsyncReset{
  val io=IO(new Bundle{

    val		scan_mode				      = Input(Bool())                              // Scan control
    val   dec_exu               = new dec_exu()
    val   exu_bp                = new exu_bp()
    //ifc-bp-mem-aln-decode
    val   exu_flush_final       = Output(UInt(1.W))         // Branch flush or flush entire pipeline
    //gpr
    val		exu_div_result			  = Output(UInt(32.W))                         // Divide result

    val  	dec_csr_rddata_d			  = Input(UInt(32.W))
    val  	lsu_nonblock_load_data	= Input(UInt(32.W))
    //lsu
    val lsu_exu                 = Flipped(new lsu_exu())
    //ifu_ifc
    val		exu_flush_path_final	= Output(UInt(31.W))                         // Target for the oldest flush source
  })

  val PREDPIPESIZE 			        = BTB_ADDR_HI - BTB_ADDR_LO + BHT_GHR_SIZE + BTB_BTAG_SIZE +1
  val ghr_x_ns 				          = Wire(UInt(BHT_GHR_SIZE.W))
  val ghr_d_ns 				          = Wire(UInt(BHT_GHR_SIZE.W))
  val ghr_d	 				            = Wire(UInt(BHT_GHR_SIZE.W))
  val i0_taken_d				        = Wire(UInt(1.W))
  val mul_valid_x				        = Wire(UInt(1.W))
  val i0_valid_d				        = Wire(UInt(1.W))
  val i0_branch_x               = Wire(UInt(1.W))
  val i0_predict_newp_d 		    = Wire(Valid(new predict_pkt_t()))
  val i0_flush_path_d			      = Wire(UInt(31.W))
  val i0_predict_p_d			      = Wire(Valid(new predict_pkt_t()))
  val i0_pp_r					          = Wire(Valid(new predict_pkt_t()))
  val i0_predict_p_x			      = Wire(Valid(new predict_pkt_t()))
  val final_predict_mp		      = Wire(Valid(new predict_pkt_t()))
  val pred_correct_npc_r		    = Wire(UInt(32.W))
  val i0_pred_correct_upper_d	  = Wire(UInt(1.W))
  val i0_flush_upper_d		      = Wire(UInt(1.W))
  io.exu_bp.exu_mp_pkt.bits.prett			:=0.U
  io.exu_bp.exu_mp_pkt.bits.br_start_error :=0.U
  io.exu_bp.exu_mp_pkt.bits.br_error  := 0.U
  io.exu_bp.exu_mp_pkt.valid			    := 0.U
  i0_pp_r.bits.toffset                := 0.U

  val x_data_en                 =  io.dec_exu.decode_exu.dec_data_en(1)
  val x_data_en_q1              =  io.dec_exu.decode_exu.dec_data_en(1) & io.dec_exu.dec_alu.dec_csr_ren_d
  val x_data_en_q2              =  io.dec_exu.decode_exu.dec_data_en(1) & io.dec_exu.decode_exu.dec_i0_branch_d
  val r_data_en                 =  io.dec_exu.decode_exu.dec_data_en(0)
  val r_data_en_q2              =  io.dec_exu.decode_exu.dec_data_en(0) & i0_branch_x
  val x_ctl_en                  =  io.dec_exu.decode_exu.dec_ctl_en(1)
  val r_ctl_en                  =  io.dec_exu.decode_exu.dec_ctl_en(0)
  val predpipe_d    = Cat(io.dec_exu.decode_exu.i0_predict_fghr_d, io.dec_exu.decode_exu.i0_predict_index_d, io.dec_exu.decode_exu.i0_predict_btag_d)

  val i0_flush_path_x 	      =rvdffpcie(i0_flush_path_d,x_data_en.asBool,reset.asAsyncReset,clock,io.scan_mode)
   i0_predict_p_x				      :=rvdffppe(i0_predict_p_d,clock,reset.asAsyncReset,x_data_en.asBool,io.scan_mode,elements= 13,io.exu_bp.exu_mp_pkt.bits.pret)
  val predpipe_x   		 	      =rvdffe(predpipe_d,x_data_en_q2.asBool,clock,io.scan_mode)
  val predpipe_r			 	      =rvdffe(predpipe_x ,r_data_en_q2.asBool,clock,io.scan_mode)
  val ghr_x					          =rvdffe(ghr_x_ns ,x_ctl_en.asBool,clock,io.scan_mode)
  val i0_pred_correct_upper_x	=rvdffe(i0_pred_correct_upper_d ,x_ctl_en.asBool,clock,io.scan_mode)
  val i0_flush_upper_x	      =rvdffe(i0_flush_upper_d ,x_ctl_en.asBool,clock,io.scan_mode)
  val i0_taken_x				      =rvdffe(i0_taken_d ,x_ctl_en.asBool,clock,io.scan_mode)
  val i0_valid_x				      =rvdffe(i0_valid_d ,x_ctl_en.asBool,clock,io.scan_mode)
  i0_pp_r                     :=rvdffppe(i0_predict_p_x,clock,reset.asAsyncReset(),r_ctl_en.asBool,io.scan_mode,elements = 13,io.exu_bp.exu_mp_pkt.bits.pret)
  val pred_temp1		          =rvdffpcie(io.dec_exu.decode_exu.pred_correct_npc_x(5,0) ,r_data_en.asBool,reset.asAsyncReset(),clock,io.scan_mode)
  val i0_pred_correct_upper_r	=rvdffppe_UInt(i0_pred_correct_upper_x ,clock,reset.asAsyncReset(),r_ctl_en.asBool,io.scan_mode,WIDTH=1)
  val i0_flush_path_upper_r	  =rvdffpcie(i0_flush_path_x ,r_data_en.asBool,reset.asAsyncReset(),clock,io.scan_mode)
  val pred_temp2				      =rvdffpcie(io.dec_exu.decode_exu.pred_correct_npc_x(30,6) ,r_data_en.asBool,reset.asAsyncReset(),clock,io.scan_mode)
  pred_correct_npc_r			    :=Cat(pred_temp2,pred_temp1)
  ghr_d			                  :=rvdffie(ghr_d_ns,clock,reset.asAsyncReset(),io.scan_mode)
  mul_valid_x		              :=rvdffie(io.dec_exu.decode_exu.mul_p.valid,clock,reset.asAsyncReset(),io.scan_mode)
  i0_branch_x		              :=rvdffie(io.dec_exu.decode_exu.dec_i0_branch_d,clock,reset.asAsyncReset(),io.scan_mode)

  val i0_rs1_bypass_en_d         = io.dec_exu.decode_exu.dec_i0_rs1_bypass_en_d(0) | io.dec_exu.decode_exu.dec_i0_rs1_bypass_en_d(1) | io.dec_exu.decode_exu.dec_i0_rs1_bypass_en_d(2) | io.dec_exu.decode_exu.dec_i0_rs1_bypass_en_d(3)
  val i0_rs2_bypass_en_d         = io.dec_exu.decode_exu.dec_i0_rs2_bypass_en_d(0) | io.dec_exu.decode_exu.dec_i0_rs2_bypass_en_d(1) | io.dec_exu.decode_exu.dec_i0_rs2_bypass_en_d(2) | io.dec_exu.decode_exu.dec_i0_rs2_bypass_en_d(3)

  val i0_rs1_bypass_data_d =	Mux1H(Seq(
    io.dec_exu.decode_exu.dec_i0_rs1_bypass_en_d(0).asBool						-> io.dec_exu.decode_exu.dec_i0_result_r,
    io.dec_exu.decode_exu.dec_i0_rs1_bypass_en_d(1).asBool 						-> io.lsu_exu.lsu_result_m,
    io.dec_exu.decode_exu.dec_i0_rs1_bypass_en_d(2).asBool						-> io.dec_exu.decode_exu.exu_i0_result_x,
    io.dec_exu.decode_exu.dec_i0_rs1_bypass_en_d(3).asBool 						-> io.lsu_nonblock_load_data
  ))
  val i0_rs2_bypass_data_d =	Mux1H(Seq(
    io.dec_exu.decode_exu.dec_i0_rs2_bypass_en_d(0).asBool						-> io.dec_exu.decode_exu.dec_i0_result_r,
    io.dec_exu.decode_exu.dec_i0_rs2_bypass_en_d(1).asBool 						-> io.lsu_exu.lsu_result_m,
    io.dec_exu.decode_exu.dec_i0_rs2_bypass_en_d(2).asBool						-> io.dec_exu.decode_exu.exu_i0_result_x,
    io.dec_exu.decode_exu.dec_i0_rs2_bypass_en_d(3).asBool 						-> io.lsu_nonblock_load_data
  ))

  val i0_rs1_d = Mux1H(Seq(
    i0_rs1_bypass_en_d.asBool 														                      -> i0_rs1_bypass_data_d,
    (!i0_rs1_bypass_en_d &  io.dec_exu.decode_exu.dec_i0_select_pc_d).asBool 		-> Cat(io.dec_exu.ib_exu.dec_i0_pc_d,0.U(1.W)),
    (!i0_rs1_bypass_en_d &  io.dec_exu.ib_exu.dec_debug_wdata_rs1_d).asBool 		-> io.dbg_cmd_wrdata,
    (!i0_rs1_bypass_en_d & !io.dec_exu.ib_exu.dec_debug_wdata_rs1_d & io.dec_exu.decode_exu.dec_i0_rs1_en_d).asBool 	-> io.dec_exu.gpr_exu.gpr_i0_rs1_d
  ))
    io.dec_exu.decode_exu.exu_csr_rs1_x			:=rvdffe(i0_rs1_d,x_data_en_q1.asBool,clock,io.scan_mode)

    val i0_rs2_d = Mux1H(Seq(
    (!i0_rs2_bypass_en_d & io.dec_exu.decode_exu.dec_i0_rs2_en_d).asBool 	-> io.dec_exu.gpr_exu.gpr_i0_rs2_d,
    (!i0_rs2_bypass_en_d).asBool						                              -> io.dec_exu.decode_exu.dec_i0_immed_d,
    (i0_rs2_bypass_en_d).asBool							                              -> i0_rs2_bypass_data_d
  ))
  dontTouch(i0_rs2_d)

  io.lsu_exu.exu_lsu_rs1_d:=Mux1H(Seq(
    (!i0_rs1_bypass_en_d & !io.dec_exu.decode_exu.dec_extint_stall & io.dec_exu.decode_exu.dec_i0_rs1_en_d & io.dec_exu.decode_exu.dec_qual_lsu_d).asBool	 	-> io.dec_exu.gpr_exu.gpr_i0_rs1_d,
    (i0_rs1_bypass_en_d & !io.dec_exu.decode_exu.dec_extint_stall & io.dec_exu.decode_exu.dec_qual_lsu_d).asBool		-> i0_rs1_bypass_data_d,
    (io.dec_exu.decode_exu.dec_extint_stall & io.dec_exu.decode_exu.dec_qual_lsu_d).asBool													-> Cat(io.dec_exu.tlu_exu.dec_tlu_meihap,0.U(2.W))
  ))

  io.lsu_exu.exu_lsu_rs2_d:=Mux1H(Seq(
    (!i0_rs2_bypass_en_d & !io.dec_exu.decode_exu.dec_extint_stall & io.dec_exu.decode_exu.dec_i0_rs2_en_d & io.dec_exu.decode_exu.dec_qual_lsu_d).asBool 	-> io.dec_exu.gpr_exu.gpr_i0_rs2_d,
    (i0_rs2_bypass_en_d & !io.dec_exu.decode_exu.dec_extint_stall & io.dec_exu.decode_exu.dec_qual_lsu_d).asBool							-> i0_rs2_bypass_data_d
  ))

  val muldiv_rs1_d=Mux1H(Seq(
    (!i0_rs1_bypass_en_d & io.dec_exu.decode_exu.dec_i0_rs1_en_d).asBool		-> io.dec_exu.gpr_exu.gpr_i0_rs1_d,
    (i0_rs1_bypass_en_d).asBool								                              -> i0_rs1_bypass_data_d
  ))

  val i_alu=Module(new exu_alu_ctl())
  i_alu.io.dec_alu <> io.dec_exu.dec_alu

  i_alu.io.scan_mode		  :=io.scan_mode
  i_alu.io.enable			    :=x_data_en
  i_alu.io.pp_in			    :=i0_predict_newp_d
  i_alu.io.flush_upper_x	:=i0_flush_upper_x
  i_alu.io.csr_rddata_in	:=io.dec_csr_rddata_d
  i_alu.io.dec_tlu_flush_lower_r	:=io.dec_exu.tlu_exu.dec_tlu_flush_lower_r
  i_alu.io.a_in			      :=i0_rs1_d.asSInt
  i_alu.io.b_in			      :=i0_rs2_d
  i_alu.io.dec_i0_pc_d		:=io.dec_exu.ib_exu.dec_i0_pc_d
  i_alu.io.i0_ap				  :=io.dec_exu.decode_exu.i0_ap
  val alu_result_x   		  =i_alu.io.result_ff
  i0_flush_upper_d   		  :=i_alu.io.flush_upper_out
  i0_flush_path_d			    :=i_alu.io.flush_path_out
  io.exu_flush_final      := i_alu.io.flush_final_out
  i0_predict_p_d			    :=i_alu.io.predict_p_out
  i0_pred_correct_upper_d :=i_alu.io.pred_correct_out

  val i_mul = Module(new exu_mul_ctl())
  i_mul.io.scan_mode	:= io.scan_mode
  i_mul.io.mul_p      := io.dec_exu.decode_exu.mul_p
  //i_mul.io.mul_p	:= VecInit.tabulate(io.dec_exu.decode_exu.mul_p.getElements.size-1)(i=>io.dec_exu.decode_exu.mul_p.getElements(i).asUInt & Fill(io.dec_exu.decode_exu.mul_p.getElements.size,io.dec_exu.decode_exu.mul_p.valid)).asTypeOf(io.dec_exu.decode_exu.mul_p) //& io.dec_exu.decode_exu.mul_p.valid
  i_mul.io.rs1_in			:= muldiv_rs1_d & Fill(32,io.dec_exu.decode_exu.mul_p.valid)
  i_mul.io.rs2_in			:=  i0_rs2_d & Fill(32,io.dec_exu.decode_exu.mul_p.valid)
  val mul_result_x		=  i_mul.io.result_x

  val i_div = Module(new exu_div_ctl())
  i_div.io.dec_div <> io.dec_exu.dec_div
  i_div.io.scan_mode	:= io.scan_mode
  i_div.io.dividend		:= muldiv_rs1_d
  i_div.io.divisor		:= i0_rs2_d
  io.exu_div_wren			:= i_div.io.exu_div_wren
  io.exu_div_result		:= i_div.io.exu_div_result

  io.dec_exu.decode_exu.exu_i0_result_x    		:= Mux(mul_valid_x.asBool, mul_result_x,  alu_result_x)
  i0_predict_newp_d                           :=  io.dec_exu.decode_exu.dec_i0_predict_p_d
  i0_predict_newp_d.bits.boffset              :=  io.dec_exu.ib_exu.dec_i0_pc_d(0)  // from the start of inst

  io.dec_exu.tlu_exu.exu_pmu_i0_br_misp       :=  i0_pp_r.bits.misp
  io.dec_exu.tlu_exu.exu_pmu_i0_br_ataken     :=  i0_pp_r.bits.ataken
  io.dec_exu.tlu_exu.exu_pmu_i0_pc4           :=  i0_pp_r.bits.pc4

  i0_valid_d               :=  i0_predict_p_d.valid  & io.dec_exu.dec_alu.dec_i0_alu_decode_d & !io.dec_exu.tlu_exu.dec_tlu_flush_lower_r
  i0_taken_d               := (i0_predict_p_d.bits.ataken & io.dec_exu.dec_alu.dec_i0_alu_decode_d)

  if(BTB_ENABLE) {
    // maintain GHR at D
    ghr_d_ns := Mux1H(Seq(
      (!io.dec_exu.tlu_exu.dec_tlu_flush_lower_r & i0_valid_d).asBool   -> Cat(ghr_d(BHT_GHR_SIZE - 2, 0), i0_taken_d),
      (!io.dec_exu.tlu_exu.dec_tlu_flush_lower_r & !i0_valid_d).asBool  -> ghr_d,
      (io.dec_exu.tlu_exu.dec_tlu_flush_lower_r).asBool                 -> ghr_x
    ))

    // maintain GHR at X
    ghr_x_ns := Mux(i0_valid_x === 1.U, Cat(ghr_x(BHT_GHR_SIZE - 2, 0), i0_taken_x), ghr_x)

    io.dec_exu.tlu_exu.exu_i0_br_valid_r  := i0_pp_r.valid
    io.dec_exu.tlu_exu.exu_i0_br_mp_r     := i0_pp_r.bits.misp
    io.exu_bp.exu_i0_br_way_r             := i0_pp_r.bits.way
    io.dec_exu.tlu_exu.exu_i0_br_hist_r   := Fill(2, i0_pp_r.valid) & i0_pp_r.bits.hist
    io.dec_exu.tlu_exu.exu_i0_br_error_r  := i0_pp_r.bits.br_error
    io.dec_exu.tlu_exu.exu_i0_br_middle_r := i0_pp_r.bits.pc4 ^ i0_pp_r.bits.boffset
    io.dec_exu.tlu_exu.exu_i0_br_start_error_r := i0_pp_r.bits.br_start_error
    io.exu_bp.exu_i0_br_fghr_r            := predpipe_r(PREDPIPESIZE - 1, BTB_ADDR_HI + BTB_BTAG_SIZE - BTB_ADDR_LO + 1)
    io.dec_exu.tlu_exu.exu_i0_br_index_r  := predpipe_r(BTB_ADDR_HI + BTB_BTAG_SIZE - BTB_ADDR_LO, BTB_BTAG_SIZE)
    io.exu_bp.exu_i0_br_index_r           := io.dec_exu.tlu_exu.exu_i0_br_index_r
    final_predict_mp                      := Mux(i0_flush_upper_x === 1.U, i0_predict_p_x, 0.U.asTypeOf(i0_predict_p_x))
    val final_predpipe_mp                 = Mux(i0_flush_upper_x === 1.U, predpipe_x, 0.U)

    val after_flush_eghr                  = Mux((i0_flush_upper_x === 1.U & !(io.dec_exu.tlu_exu.dec_tlu_flush_lower_r === 1.U)), ghr_d, ghr_x)

    io.exu_bp.exu_mp_pkt.valid        := final_predict_mp.valid
    io.exu_bp.exu_mp_pkt.bits.way     := final_predict_mp.bits.way
    io.exu_bp.exu_mp_pkt.bits.misp    := final_predict_mp.bits.misp
    io.exu_bp.exu_mp_pkt.bits.pcall   := final_predict_mp.bits.pcall
    io.exu_bp.exu_mp_pkt.bits.pja     := final_predict_mp.bits.pja
    io.exu_bp.exu_mp_pkt.bits.pret    := final_predict_mp.bits.pret
    io.exu_bp.exu_mp_pkt.bits.ataken  := final_predict_mp.bits.ataken
    io.exu_bp.exu_mp_pkt.bits.boffset := final_predict_mp.bits.boffset
    io.exu_bp.exu_mp_pkt.bits.pc4     := final_predict_mp.bits.pc4
    io.exu_bp.exu_mp_pkt.bits.hist    := final_predict_mp.bits.hist(1, 0)
    io.exu_bp.exu_mp_pkt.bits.toffset := final_predict_mp.bits.toffset(11, 0)
    io.exu_bp.exu_mp_fghr             := after_flush_eghr
    io.exu_bp.exu_mp_index            := final_predpipe_mp(PREDPIPESIZE - BHT_GHR_SIZE - 1, BTB_BTAG_SIZE)
    io.exu_bp.exu_mp_btag             := final_predpipe_mp(BTB_BTAG_SIZE - 1, 0)
    io.exu_bp.exu_mp_eghr             := final_predpipe_mp(PREDPIPESIZE - 1, BTB_ADDR_HI - BTB_ADDR_LO + BTB_BTAG_SIZE + 1) // mp ghr for bht write
  }
  else {

    ghr_d_ns := 0.U
    ghr_x_ns := 0.U
    io.exu_bp.exu_mp_pkt    := 0.U
    io.exu_bp.exu_mp_eghr   := 0.U
    io.exu_bp.exu_mp_fghr   := 0.U
    io.exu_bp.exu_mp_index  := 0.U
    io.exu_bp.exu_mp_btag   := 0.U
    io.dec_exu.tlu_exu.exu_i0_br_hist_r   := 0.U
    io.dec_exu.tlu_exu.exu_i0_br_error_r  := 0.U
    io.dec_exu.tlu_exu.exu_i0_br_start_error_r := 0.U
    io.dec_exu.tlu_exu.exu_i0_br_index_r  := 0.U
    io.dec_exu.tlu_exu.exu_i0_br_valid_r  := 0.U
    io.dec_exu.tlu_exu.exu_i0_br_mp_r     := 0.U
    io.dec_exu.tlu_exu.exu_i0_br_middle_r := 0.U
    io.exu_bp.exu_i0_br_fghr_r  := 0.U
    io.exu_bp.exu_i0_br_way_r   := 0.U
  }
  io.exu_flush_path_final	:= Mux1H(Seq(
    io.dec_exu.tlu_exu.dec_tlu_flush_lower_r.asBool -> io.dec_exu.tlu_exu.dec_tlu_flush_path_r,
    (~io.dec_exu.tlu_exu.dec_tlu_flush_lower_r & i0_flush_upper_d).asBool -> i0_flush_path_d))

  io.dec_exu.tlu_exu.exu_npc_r			:= Mux(i0_pred_correct_upper_r===1.U,  pred_correct_npc_r, i0_flush_path_upper_r)
}
object exu_main extends App {
  println((new chisel3.stage.ChiselStage).emitVerilog(new exu()))
}

    val		exu_div_wren			    = Output(UInt(1.W))                          // Divide write enable to GPR
    //debug


package exu

import chisel3.{util, _}
import chisel3.experimental.chiselName
import chisel3.util._
import include._
import lib._

@chiselName
class exu_div_ctl extends Module with RequireAsyncReset with lib {
  val io = IO(new Bundle {
    val scan_mode = Input(Bool())
    val dividend = Input(UInt(32.W))
    val divisor = Input(UInt(32.W))
    val exu_div_result = Output(UInt(32.W))
    val exu_div_wren = Output(UInt(1.W))
    val dec_div = new dec_div()
  })

  val out_raw =WireInit(0.U(32.W))
  io.exu_div_result := Fill(32,io.exu_div_wren) & out_raw
if(!DIV_NEW) {
  val divider_old = Module(new exu_div_existing_1bit_cheapshortq())
  divider_old.io.scan_mode    := io.scan_mode
  divider_old.io.cancel       := io.dec_div.dec_div_cancel
  divider_old.io.valid_in     := io.dec_div.div_p.valid
  divider_old.io.signed_in    := ~io.dec_div.div_p.bits.unsign
  divider_old.io.rem_in       := io.dec_div.div_p.bits.rem
  divider_old.io.dividend_in  := io.dividend
  divider_old.io.divisor_in   := io.divisor
  out_raw                 := divider_old.io.data_out
  io.exu_div_wren         := divider_old.io.valid_out
}
  if(DIV_NEW & DIV_BIT==1) {
    val divider_new1 = Module(new exu_div_new_1bit_fullshortq())
    divider_new1.io.scan_mode    := io.scan_mode
    divider_new1.io.cancel       := io.dec_div.dec_div_cancel
    divider_new1.io.valid_in     := io.dec_div.div_p.valid
    divider_new1.io.signed_in    := ~io.dec_div.div_p.bits.unsign
    divider_new1.io.rem_in       := io.dec_div.div_p.bits.rem
    divider_new1.io.dividend_in  := io.dividend
    divider_new1.io.divisor_in   := io.divisor
    out_raw                 := divider_new1.io.data_out
    io.exu_div_wren         := divider_new1.io.valid_out
  }
  if(DIV_NEW & DIV_BIT==2) {
    val divider_new2 = Module(new exu_div_new_2bit_fullshortq())
    divider_new2.io.scan_mode    := io.scan_mode
    divider_new2.io.cancel       := io.dec_div.dec_div_cancel
    divider_new2.io.valid_in     := io.dec_div.div_p.valid
    divider_new2.io.signed_in    := ~io.dec_div.div_p.bits.unsign
    divider_new2.io.rem_in       := io.dec_div.div_p.bits.rem
    divider_new2.io.dividend_in  := io.dividend
    divider_new2.io.divisor_in   := io.divisor
    out_raw                 := divider_new2.io.data_out
    io.exu_div_wren         := divider_new2.io.valid_out
  }
  if(DIV_NEW & DIV_BIT==3) {
    val divider_new3 = Module(new exu_div_new_3bit_fullshortq())
    divider_new3.io.scan_mode    := io.scan_mode
    divider_new3.io.cancel       := io.dec_div.dec_div_cancel
    divider_new3.io.valid_in     := io.dec_div.div_p.valid
    divider_new3.io.signed_in    := ~io.dec_div.div_p.bits.unsign
    divider_new3.io.rem_in       := io.dec_div.div_p.bits.rem
    divider_new3.io.dividend_in  := io.dividend
    divider_new3.io.divisor_in   := io.divisor
    out_raw                 := divider_new3.io.data_out
    io.exu_div_wren         := divider_new3.io.valid_out
  }
  if(DIV_NEW & DIV_BIT==4) {
    val divider_new4 = Module(new exu_div_new_4bit_fullshortq())
    divider_new4.io.scan_mode    := io.scan_mode
    divider_new4.io.cancel       := io.dec_div.dec_div_cancel
    divider_new4.io.valid_in     := io.dec_div.div_p.valid
    divider_new4.io.signed_in    := ~io.dec_div.div_p.bits.unsign
    divider_new4.io.rem_in       := io.dec_div.div_p.bits.rem
    divider_new4.io.dividend_in  := io.dividend
    divider_new4.io.divisor_in   := io.divisor
    out_raw                 := divider_new4.io.data_out
    io.exu_div_wren         := divider_new4.io.valid_out
  }
}
object div_main extends App {
  println((new chisel3.stage.ChiselStage).emitVerilog(new exu_div_ctl()))
}
////////////////////////////////////////// OLD DIVIDER /////////////////////////////////////
class exu_div_existing_1bit_cheapshortq extends Module with RequireAsyncReset with lib {
  val io = IO(new Bundle{
    val scan_mode             = Input(Bool())
    val cancel                = Input(Bool())
    val valid_in              = Input(Bool())
    val signed_in             = Input(Bool())
    val rem_in                = Input(Bool())
    val dividend_in           = Input(UInt(32.W))
    val divisor_in            = Input(UInt(32.W))
    val data_out              = Output(UInt(32.W))
    val valid_out             = Output(UInt(1.W))
  })
  val run_state            = WireInit(0.U(1.W))
  val count                = WireInit(0.U(6.W))
  val m_ff                 = WireInit(0.U(33.W))
  val q_in                 = WireInit(0.U(33.W))
  val q_ff                 = WireInit(0.U(33.W))
  val a_in                 = WireInit(0.U(33.W))
  val a_ff                 = WireInit(0.U(33.W))
  val m_eff                = WireInit(0.U(33.W))
  val dividend_neg_ff      = WireInit(0.U(1.W))
  val divisor_neg_ff       = WireInit(0.U(1.W))
  val dividend_comp        = WireInit(0.U(32.W))
  val q_ff_comp            = WireInit(0.U(32.W))
  val a_ff_comp            = WireInit(0.U(32.W))
  val sign_ff              = WireInit(0.U(1.W))
  val rem_ff               = WireInit(0.U(1.W))
  val add                  = WireInit(0.U(1.W))
  val a_eff                = WireInit(0.U(33.W))
  val a_eff_shift          = WireInit(0.U(65.W))
  val rem_correct          = WireInit(0.U(1.W))
  val valid_ff_x           = WireInit(0.U(1.W))
  val finish_ff            = WireInit(0.U(1.W))
  val smallnum_case_ff     = WireInit(0.U(1.W))
  val smallnum_ff          = WireInit(0.U(4.W))
  val smallnum_case        = WireInit(0.U(1.W))
  val count_in             = WireInit(0.U(6.W))
  val dividend_eff         = WireInit(0.U(32.W))
  val a_shift              = WireInit(0.U(33.W))
  val shortq               = WireInit(0.U(6.W))
  val valid_x = valid_ff_x & !io.cancel

  // START - short circuit logic for small numbers {{
  // small number divides - any 4b / 4b is done in 1 cycle (divisor != 0)
  // smallnum case does not cover divide by 0

  smallnum_case :=  ((q_ff(31,4) === 0.U) & (m_ff(31,4) === 0.U) & (m_ff(31,0) =/= 0.U) & !rem_ff & valid_x) |
    ((q_ff(31,0) === 0.U) & (m_ff(31,0) =/= 0.U) & !rem_ff & valid_x)

  def pat(x : List[Int], y : List[Int]) = {
    val pat_a = (0 until x.size).map(i=> if(x(i)>=0) q_ff(x(i)) else !q_ff(x(i).abs)).reduce(_&_)

val pat_b = (0 until y.size).map(i=> if(y(i)>=0) m_ff(y(i)) else !m_ff(y(i).abs)).reduce(_&_)


package exu

import chisel3._
import chisel3.util._
import include._
import lib._
import chisel3.stage.ChiselStage

class exu_mul_ctl extends Module with RequireAsyncReset with lib {
  val io = IO(new Bundle{
    val scan_mode          = Input(Bool())
    val mul_p              = Flipped(Valid(new mul_pkt_t ))
    val rs1_in             = Input(UInt(32.W))
    val rs2_in             = Input(UInt(32.W))
    val result_x           = Output(UInt(32.W))
  })

  val rs1_ext_in           = WireInit(SInt(33.W), 0.S)
  val rs2_ext_in           = WireInit(SInt(33.W), 0.S)
  val rs1_x                = WireInit(SInt(33.W), 0.S)
  val rs2_x                = WireInit(SInt(33.W), 0.S)
  val prod_x               = WireInit(SInt(66.W), 0.S)
  val low_x                = WireInit(0.U(1.W))

  // *** Start - BitManip ***
  // ZBE
  val        ap_bext = WireInit(Bool(),0.B)
  val        ap_bdep = WireInit(Bool(),0.B)

  // ZBC
  val        ap_clmul   = WireInit(Bool(),0.B)
  val        ap_clmulh  = WireInit(Bool(),0.B)
  val        ap_clmulr  = WireInit(Bool(),0.B)

  // ZBP
  val         ap_grev    = WireInit(Bool(),0.B)
  val         ap_gorc    = WireInit(Bool(),0.B)
  val         ap_shfl    = WireInit(Bool(),0.B)
  val         ap_unshfl  = WireInit(Bool(),0.B)

  // ZBR
  val         ap_crc32_b   = WireInit(Bool(),0.B)
  val         ap_crc32_h   = WireInit(Bool(),0.B)
  val         ap_crc32_w   = WireInit(Bool(),0.B)
  val         ap_crc32c_b  = WireInit(Bool(),0.B)
  val         ap_crc32c_h  = WireInit(Bool(),0.B)
  val         ap_crc32c_w  = WireInit(Bool(),0.B)

  // ZBF
  val         ap_bfp = WireInit(Bool(),0.B)

  if (BITMANIP_ZBE == 1) {
    ap_bext         :=  io.mul_p.bits.bext
    ap_bdep         :=  io.mul_p.bits.bdep

  }
  else{
    ap_bext         :=  0.U
    ap_bdep         :=  0.U
  }

  if (BITMANIP_ZBC == 1) {
    ap_clmul        :=  io.mul_p.bits.clmul
    ap_clmulh       :=  io.mul_p.bits.clmulh
    ap_clmulr       :=  io.mul_p.bits.clmulr
  }
  else{
    ap_clmul        :=  0.U
    ap_clmulh       :=  0.U
    ap_clmulr       :=  0.U
  }

  if (BITMANIP_ZBP == 1) {
    ap_grev         :=  io.mul_p.bits.grev
    ap_gorc         :=  io.mul_p.bits.gorc
    ap_shfl         :=  io.mul_p.bits.shfl
    ap_unshfl       :=  io.mul_p.bits.unshfl
  }
  else{
    ap_grev         :=  0.U
    ap_gorc         :=  0.U
    ap_shfl         :=  0.U
    ap_unshfl       :=  0.U
  }

  if (BITMANIP_ZBR == 1) {
    ap_crc32_b      :=  io.mul_p.bits.crc32_b
    ap_crc32_h      :=  io.mul_p.bits.crc32_h
    ap_crc32_w      :=  io.mul_p.bits.crc32_w
    ap_crc32c_b     :=  io.mul_p.bits.crc32c_b
    ap_crc32c_h     :=  io.mul_p.bits.crc32c_h
    ap_crc32c_w     :=  io.mul_p.bits.crc32c_w
  }
  else{
    ap_crc32_b      :=  0.U
    ap_crc32_h      :=  0.U
    ap_crc32_w      :=  0.U
    ap_crc32c_b     :=  0.U
    ap_crc32c_h     :=  0.U
    ap_crc32c_w     :=  0.U
  }

  if (BITMANIP_ZBF == 1) {
    ap_bfp          :=  io.mul_p.bits.bfp
  }
  else{
    ap_bfp          :=  0.U
  }

  // *** End   - BitManip ***

  val mul_x_enable         = io.mul_p.valid
  val bit_x_enable         = io.mul_p.valid
  rs1_ext_in := Cat(io.mul_p.bits.rs1_sign & io.rs1_in(31),io.rs1_in).asSInt
  rs2_ext_in := Cat(io.mul_p.bits.rs2_sign & io.rs2_in(31),io.rs2_in).asSInt

  low_x := rvdffe (io.mul_p.bits.low, mul_x_enable.asBool,clock,io.scan_mode)
  rs1_x := rvdffe(rs1_ext_in, mul_x_enable.asBool,clock,io.scan_mode)
  rs2_x := rvdffe (rs2_ext_in, mul_x_enable.asBool,clock,io.scan_mode)

  prod_x := rs1_x  *  rs2_x

  // * * * * * * * * * * * * * * * * * *  BitManip  :  BEXT, BDEP   * * * * * * * * * * * * * * * * * *

  // *** BEXT == "gather"  ***

  def one_cal (ind:Int) : UInt = if (ind == 0) io.rs2_in(ind) else (0 to ind).map(io.rs2_in(_).asUInt).reduce(_+&_)
  val bext_d = (0 until 32).map(i=> MuxCase(false.B, (0 until 32).map(j=> (one_cal(j) === (i+1).U).asBool -> io.rs1_in(j).asUInt))).reverse.reduce(Cat(_,_))

  // *** BDEP == "scatter" ***
  val bdep_d =(0 until 32).map(j => Mux((io.rs2_in(j) === 1.U), io.rs1_in(one_cal(j)-1.U),0.U)).reverse.reduce(Cat(_,_))
  // * * * * * * * * * * * * * * * * * *  BitManip  :  CLMUL, CLMULH, CLMULR  * * * * * * * * * * * * *

  val clmul_raw_d = WireInit(UInt(63.W),0.U)
  clmul_raw_d := (1 until 31).map(i => Fill(63,io.rs2_in(i)) & Cat(Fill(31-i,0.U),io.rs1_in(31,0),Fill(i,0.U))).reduce(_^_) ^ ( Fill(63,io.rs2_in(0)) & Cat(Fill(31,0.U),io.rs1_in) ) ^ ( Fill(63,io.rs2_in(31)) & Cat(io.rs1_in,Fill(31,0.U)) )

  // * * * * * * * * * * * * * * * * * *  BitManip  :  GREV         * * * * * * * * * * * * * * * * * *

  // uint32_t grev32(uint32_t rs1, uint32_t rs2)
  // {
  //     uint32_t x = rs1;
  //     int shamt = rs2 & 31;
  //
  //     if (shamt &  1)  x = ( (x & 0x55555555) <<  1) | ( (x & 0xAAAAAAAA) >>  1);
  //     if (shamt &  2)  x = ( (x & 0x33333333) <<  2) | ( (x & 0xCCCCCCCC) >>  2);
  //     if (shamt &  4)  x = ( (x & 0x0F0F0F0F) <<  4) | ( (x & 0xF0F0F0F0) >>  4);
  //     if (shamt &  8)  x = ( (x & 0x00FF00FF) <<  8) | ( (x & 0xFF00FF00) >>  8);
  //     if (shamt & 16)  x = ( (x & 0x0000FFFF) << 16) | ( (x & 0xFFFF0000) >> 16);
  //
  //     return x;
  //  }

  val grev1_d = Mux(io.rs2_in(0), Range(0, 31, 2).map(i=> Cat(io.rs1_in(i),io.rs1_in(i+1))).reverse.reduce(Cat(_,_)),  io.rs1_in)

  val grev2_d = Mux(io.rs2_in(1), Range(0, 31, 4).map(i=> Cat(grev1_d(i+1,i),grev1_d(i+1+2,i+2))).reverse.reduce(Cat(_,_)) ,  grev1_d(31,0))

  val grev4_d = Mux(io.rs2_in(2), Range(0, 31, 8).map(i=> Cat(grev2_d(i+3,i),grev2_d(i+3+4,i+4))).reverse.reduce(Cat(_,_)) ,  grev2_d(31,0))

  val grev8_d = Mux(io.rs2_in(3), Range(0, 31, 16).map(i=> Cat(grev4_d(i+7,i),grev4_d(i+7+8,i+8))).reverse.reduce(Cat(_,_)),  grev4_d(31,0))

  val grev_d  = Mux(io.rs2_in(4),  Cat(grev8_d(15,0),grev8_d(31,16)), grev8_d(31,0)  )

  // * * * * * * * * * * * * * * * * * *  BitManip  :  GORC         * * * * * * * * * * * * * * * * * *

  // uint32_t gorc32(uint32_t rs1, uint32_t rs2)
  // {
  //     uint32_t x = rs1;
  //     int shamt = rs2 & 31;
  //
  //     if (shamt &  1)  x |= ( (x & 0x55555555) <<  1) | ( (x & 0xAAAAAAAA) >>  1);
  //     if (shamt &  2)  x |= ( (x & 0x33333333) <<  2) | ( (x & 0xCCCCCCCC) >>  2);
  //     if (shamt &  4)  x |= ( (x & 0x0F0F0F0F) <<  4) | ( (x & 0xF0F0F0F0) >>  4);
  //     if (shamt &  8)  x |= ( (x & 0x00FF00FF) <<  8) | ( (x & 0xFF00FF00) >>  8);
  //     if (shamt & 16)  x |= ( (x & 0x0000FFFF) << 16) | ( (x & 0xFFFF0000) >> 16);
  //
  //     return x;
  //  }

  val gorc1_d  = ( Fill(32,io.rs2_in(0)) & Range(0, 31, 2).map(i=> Cat(io.rs1_in(i),io.rs1_in(i+1))).reverse.reduce(Cat(_,_)) ) | io.rs1_in

  val gorc2_d  = ( Fill(32,io.rs2_in(1)) & Range(0, 31, 4).map(i=> Cat(gorc1_d(i+1,i),gorc1_d(i+1+2,i+2))).reverse.reduce(Cat(_,_)) ) | gorc1_d

  val gorc4_d  = ( Fill(32,io.rs2_in(2)) & Range(0, 31, 8).map(i=> Cat(gorc2_d(i+3,i),gorc2_d(i+3+4,i+4))).reverse.reduce(Cat(_,_)) ) | gorc2_d

  val gorc8_d  = ( Fill(32,io.rs2_in(3)) & Range(0, 31, 16).map(i=> Cat(gorc4_d(i+7,i),gorc4_d(i+7+8,i+8))).reverse.reduce(Cat(_,_)) ) | gorc4_d

  val gorc_d   = ( Fill(32,io.rs2_in(4)) & Cat(gorc8_d(15,0),gorc8_d(31,16)) ) | gorc8_d

  // * * * * * * * * * * * * * * * * * *  BitManip  :  SHFL, UNSHLF * * * * * * * * * * * * * * * * * *

  // uint32_t shuffle32_stage (uint32_t src, uint32_t maskL, uint32_t maskR, int N)
  // {
  //     uint32_t x  = src & ~(maskL | maskR);
  //     x          |= ((src << N) & maskL) | ((src >> N) & maskR);
  //     return x;
  // }
  //
  //
  //
  // uint32_t shfl32(uint32_t rs1, uint32_t rs2)
  // {
  //     uint32_t x = rs1;
  //     int shamt = rs2 & 15
  //
  //     if (shamt & 8)  x = shuffle32_stage(x, 0x00ff0000, 0x0000ff00, 8);
  //     if (shamt & 4)  x = shuffle32_stage(x, 0x0f000f00, 0x00f000f0, 4);
  //     if (shamt & 2)  x = shuffle32_stage(x, 0x30303030, 0xc0c0c0c0, 2);
  //     if (shamt & 1)  x = shuffle32_stage(x, 0x44444444, 0x22222222, 1);
  //
  //     return x;
  // }

  val shfl8_d = Mux(io.rs2_in(3),Range(0, 9,8).map(i=> Cat(io.rs1_in(i+7+16,i+16),io.rs1_in(i+7,i))).reverse.reduce(Cat(_,_)) ,io.rs1_in)

  val shfl4_d = Mux(io.rs2_in(2),Range(0, 13,4).map(i=> if(i<8) Cat(shfl8_d(i+3+8,i+8),shfl8_d(i+3,i))else  Cat(shfl8_d(i+3+8+8,i+8+8),shfl8_d(i+3+8,i+8))).reverse.reduce(Cat(_,_)), shfl8_d)

  val shfl_d  = Mux(io.rs2_in(0), Range(0, 16,1).map(i=> if(i<2) Cat(shfl2_d(i+2),shfl2_d(i))else if(i<4)  Cat(shfl2_d(i+4),shfl2_d(i+2))else if(i<6)  Cat(shfl2_d(i+6),shfl2_d(i+4))else if(i<8)  Cat(shfl2_d(i+8),shfl2_d(i+6))else if(i<10)  Cat(shfl2_d(i+10),shfl2_d(i+8))else if(i<12)  Cat(shfl2_d(i+12),shfl2_d(i+10))else if(i<14)  Cat(shfl2_d(i+14),shfl2_d(i+12))else  Cat(shfl2_d(i+16),shfl2_d(i+14))).reverse.reduce(Cat(_,_)),  shfl2_d)

  //  // uint32_t unshfl32(uint32_t rs1, uint32_t rs2)
  //  // {
  //  //     uint32_t x = rs1;
  //  //     int shamt = rs2 & 15
  //  //
  //  //     if (shamt & 1)  x = shuffle32_stage(x, 0x44444444, 0x22222222, 1);
  //  //     if (shamt & 2)  x = shuffle32_stage(x, 0x30303030, 0xc0c0c0c0, 2);
  //  //     if (shamt & 4)  x = shuffle32_stage(x, 0x0f000f00, 0x00f000f0, 4);
  //  //     if (shamt & 8)  x = shuffle32_stage(x, 0x00ff0000, 0x0000ff00, 8);
  //  //
  //  //     return x;
  //  // }
  //
  //
  val unshfl1_d    = Mux(io.rs2_in(0) , Range(0, 16,1).map(i=> if(i<2) Cat(io.rs1_in(i+2),io.rs1_in(i))else if(i<4)  Cat(io.rs1_in(i+4),io.rs1_in(i+2))else if(i<6)  Cat(io.rs1_in(i+6),io.rs1_in(i+4))else if(i<8)  Cat(io.rs1_in(i+8),io.rs1_in(i+6))else if(i<10)  Cat(io.rs1_in(i+10),io.rs1_in(i+8))else if(i<12)  Cat(io.rs1_in(i+12),io.rs1_in(i+10))else if(i<14)  Cat(io.rs1_in(i+14),io.rs1_in(i+12))else  Cat(io.rs1_in(i+16),io.rs1_in(i+14))).reverse.reduce(Cat(_,_)) ,  io.rs1_in)

  val unshfl2_d     =Mux(io.rs2_in(1) , Range(0, 15,2).map(i=> if(i<4)Cat(unshfl1_d(i+1+4,i+4),unshfl1_d(i+1,i))else if(i<8)Cat(unshfl1_d(i+9,i+8),unshfl1_d(i+5,i+4))else if(i<12)Cat(unshfl1_d(i+13,i+12),unshfl1_d(i+9,i+8))else Cat(unshfl1_d(i+17,i+16),unshfl1_d(i+13,i+12))).reverse.reduce(Cat(_,_)) ,  unshfl1_d)

  val unshfl4_d    = Mux(io.rs2_in(2) , Range(0, 13,4).map(i=> if(i<8) Cat(unshfl2_d(i+3+8,i+8),unshfl2_d(i+3,i))else  Cat(unshfl2_d(i+3+8+8,i+8+8),unshfl2_d(i+3+8,i+8))).reverse.reduce(Cat(_,_)) ,  unshfl2_d)

  val unshfl_d     = Mux(io.rs2_in(3) , Range(0, 9,8).map(i=> Cat(unshfl4_d(i+7+16,i+16),unshfl4_d(i+7,i))).reverse.reduce(Cat(_,_)) , unshfl4_d)

  // * * * * * * * * * * * * * * * * * *  BitManip  :  BFP          * * * * * * * * * * * * * * * * * *

  val bfp_len           =  Cat(io.rs2_in(27,24) === 0.U,io.rs2_in(27,24)) // If LEN field is zero, then LEN=16
  val bfp_off      =  io.rs2_in(20,16)

  val bfp_len_mask_    =  "hffff_ffff".U(32.W)  <<  bfp_len
  val bfp_preshift_data  =  io.rs2_in(15,0) & ~bfp_len_mask_(15,0)

  val bfp_shift_data   = Cat(Fill(16,0.U),bfp_preshift_data(15,0), Fill(16,0.U),bfp_preshift_data(15,0))  <<  bfp_off
  val bfp_shift_mask   = Cat(bfp_len_mask_(31,0), bfp_len_mask_(31,0))  <<  bfp_off

  val bfp_result_d     = bfp_shift_data(63,32) | (io.rs1_in & bfp_shift_mask(63,32))

  // * * * * * * * * * * * * * * * * * *  BitManip  :  CRC32, CRC32c  * * * * * * * * * * * * * * * * *

  // ***  computed from   https: //crccalc.com  ***
  //
  // "a" is 8'h61 = 8'b0110_0001    (8'h61 ^ 8'hff = 8'h9e)
  //
  // Input must first be XORed with 32'hffff_ffff
  //
  //
  // CRC32
  //
  // Input    Output        Input      Output
  // -----   --------      --------   --------
  // "a"     e8b7be43      ffffff9e   174841bc
  // "aa"    078a19d7      ffff9e9e   f875e628
  // "aaaa"  ad98e545      9e9e9e9e   5267a1ba
  //
  //
  //
  // CRC32c
  //
  // Input    Output        Input      Output
  // -----   --------      --------   --------
  // "a"     c1d04330      ffffff9e   3e2fbccf
  // "aa"    f1f2dac2      ffff9e9e   0e0d253d
  // "aaaa"  6a52eeb0      9e9e9e9e   95ad114f

  val crc32_all              =  ap_crc32_b  | ap_crc32_h  | ap_crc32_w | ap_crc32c_b | ap_crc32c_h | ap_crc32c_w

  val crc32_poly_rev   =  "hEDB88320".U(32.W)    // bit reverse of 32'h04C11DB7
  val crc32c_poly_rev  =  "h82F63B78".U(32.W)    // bit reverse of 32'h1EDC6F41

  val crc32_bd            =  Wire(Vec(9,UInt(32.W)))
  crc32_bd(0) := io.rs1_in
  for(i <- 1 to 8) {
    crc32_bd(i) := (crc32_bd(i-1) >> 1) ^ (crc32_poly_rev & Fill(32,crc32_bd(i-1)(0)))//io.rs1_in
  }

  val crc32_hd            =  Wire(Vec(17,UInt(32.W)))
  crc32_hd(0) := io.rs1_in
  for(i <- 1 to 16) {
    crc32_hd(i) := (crc32_hd(i-1) >> 1) ^ (crc32_poly_rev & Fill(32,crc32_hd(i-1)(0)))//io.rs1_in
  }

  val crc32_wd            =  Wire(Vec(33,UInt(32.W)))
  crc32_wd(0) := io.rs1_in
  for(i <- 1 to 32) {
    crc32_wd(i) := (crc32_wd(i-1) >> 1) ^ (crc32_poly_rev & Fill(32,crc32_wd(i-1)(0)))//io.rs1_in
  }
  /////////////////////////////////////////////////////////////////////////////////////////

  val crc32c_bd            =  Wire(Vec(9,UInt(32.W)))
  crc32c_bd(0) := io.rs1_in
  for(i <- 1 to 8) {
    crc32c_bd(i) := (crc32c_bd(i-1) >> 1) ^ (crc32c_poly_rev & Fill(32,crc32c_bd(i-1)(0)))//io.rs1_in
  }

  val crc32c_hd            =  Wire(Vec(17,UInt(32.W)))
  crc32c_hd(0) := io.rs1_in
  for(i <- 1 to 16) {
    crc32c_hd(i) := (crc32c_hd(i-1) >> 1) ^ (crc32c_poly_rev & Fill(32,crc32c_hd(i-1)(0)))//io.rs1_in
  }

  val crc32c_wd            =  Wire(Vec(33,UInt(32.W)))
  crc32c_wd(0) := io.rs1_in
  for(i <- 1 to 32) {
    crc32c_wd(i) := (crc32c_wd(i-1) >> 1) ^ (crc32c_poly_rev & Fill(32,crc32c_wd(i-1)(0)))//io.rs1_in
  }

  // * * * * * * * * * * * * * * * * * *  BitManip  :  Common logic * * * * * * * * * * * * * * * * * *

  val bitmanip_sel_d         =  ap_bext | ap_bdep | ap_clmul | ap_clmulh | ap_clmulr | ap_grev | ap_gorc | ap_shfl | ap_unshfl | crc32_all | ap_bfp

  val bitmanip_d       = Mux1H(Seq(
    ap_bext     ->       bext_d(31,0)                         ,
    ap_bdep     ->       bdep_d(31,0)                         ,
    ap_clmul    ->       clmul_raw_d(31,0)                    ,
    ap_clmulh   ->       Cat(0.U(1.W),clmul_raw_d(62,32))     ,
    ap_clmulr   ->       clmul_raw_d(62,31)                   ,
    ap_grev     ->       grev_d(31,0)                         ,
    ap_gorc     ->       gorc_d(31,0)                         ,
    ap_shfl     ->       shfl_d(31,0)                         ,
    ap_unshfl   ->       unshfl_d(31,0)                       ,
    ap_crc32_b  ->       crc32_bd(8)(31,0)                       ,
    ap_crc32_h  ->       crc32_hd(16)(31,0)                       ,
    ap_crc32_w  ->       crc32_wd(32)(31,0)                       ,
    ap_crc32c_b ->       crc32c_bd(8)(31,0)                      ,
    ap_crc32c_h ->       crc32c_hd(16)(31,0)                      ,
    ap_crc32c_w ->       crc32c_wd(32)(31,0)                      ,
    ap_bfp      ->       bfp_result_d(31,0) ))

  val bitmanip_sel_x = rvdffe(bitmanip_sel_d,bit_x_enable,clock,io.scan_mode)
  val bitmanip_x = rvdffe(bitmanip_d,bit_x_enable,clock,io.scan_mode)

  io.result_x :=  (Fill(32,~bitmanip_sel_x & ~low_x) & prod_x(63,32) )    |
    (Fill(32,~bitmanip_sel_x &  low_x) & prod_x(31,0)  )    |
    bitmanip_x

}
object mul extends App {
  println((new ChiselStage).emitVerilog(new exu_mul_ctl))}


package lib

import chisel3._
import chisel3.util._
import include._

class axi4_to_ahb_IO(val TAG : Int) extends Bundle {

  val free_clk = Input(Clock())
  val scan_mode = Input(Bool())
  val bus_clk_en = Input(Bool())
  val clk_override = Input(Bool())
  val dec_tlu_force_halt = Input(Bool())
  // AXI-4 signals
  val axi = Flipped(new axi_channels(TAG))
  // AHB-Lite signals
  val ahb = new ahb_channel()
}

class axi4_to_ahb(val TAG : Int = 3) extends Module with lib with RequireAsyncReset {
  val io = IO(new axi4_to_ahb_IO(TAG))
  // Create bus synchronized version of force halt
  val dec_tlu_force_halt_bus_q = WireInit(Bool(), init = false.B)
  val dec_tlu_force_halt_bus = io.dec_tlu_force_halt | dec_tlu_force_halt_bus_q
  val dec_tlu_force_halt_bus_ns = !io.bus_clk_en & dec_tlu_force_halt_bus

  val buf_rst = WireInit(0.U(1.W))
  buf_rst := dec_tlu_force_halt_bus
  io.ahb.out.htrans := 0.U
  val buf_state_en = WireInit(Bool(), init = false.B)
  val bus_clk = Wire(Clock())
  val ahbm_data_clk = Wire(Clock())
  val idle :: cmd_rd :: cmd_wr :: data_rd :: data_wr :: done :: stream_rd :: stream_err_rd :: Nil = Enum(8)
  val buf_state = WireInit(0.U(3.W))
  val buf_nxtstate = WireInit(0.U(3.W))
  buf_state := rvdffsc_fpga(buf_nxtstate, buf_state_en.asBool(), buf_rst, bus_clk, io.bus_clk_en, clock)
  //logic signals
  val slave_valid = WireInit(Bool(), init = false.B)
  val slave_ready = WireInit(Bool(), init = false.B)
  val slave_tag = WireInit(0.U(TAG.W)) // [TAG-1:0]
  val slave_rdata = WireInit(0.U(64.W)) // [63:0]
  val slave_opc = WireInit(0.U(4.W)) // [3:0]
  val wrbuf_en = WireInit(Bool(), init = false.B)
  val wrbuf_data_en = WireInit(Bool(), init = false.B)
  val wrbuf_cmd_sent = WireInit(Bool(), init = false.B)
  val wrbuf_rst = WireInit(Bool(), init = false.B)
  val wrbuf_vld = WireInit(Bool(), init = false.B)
  val wrbuf_data_vld = WireInit(Bool(), init = false.B)
  val wrbuf_tag = WireInit(0.U(TAG.W)) // [TAG-1:0]
  val wrbuf_size = WireInit(0.U(3.W)) // [2:0]
  val wrbuf_addr = WireInit(0.U(32.W)) // [31:0]
  val wrbuf_data = WireInit(0.U(64.W)) // [63:0]
  val wrbuf_byteen = WireInit(0.U(8.W)) // [7:0]

  val master_valid = WireInit(Bool(), init = false.B)
  val master_ready = WireInit(0.U(1.W))
  val master_tag = WireInit(0.U(TAG.W)) // [TAG-1:0]
  val master_addr = WireInit(0.U(32.W)) // [31:0]
  val master_wdata = WireInit(0.U(64.W)) // [63:0]
  val master_size = WireInit(0.U(3.W)) // [2:0]
  val master_opc = WireInit(0.U(3.W)) // [2:0]
  val master_byteen = WireInit(0.U(8.W)) // [7:0]
  // Buffer signals (one entry buffer)
  val buf_addr = WireInit(0.U(32.W)) // [31:0]
  val buf_size = WireInit(0.U(2.W)) // [1:0]
  val buf_write = WireInit(Bool(), init = false.B)
  val buf_byteen = WireInit(0.U(8.W)) // [7:0]
  val buf_aligned = WireInit(Bool(), init = false.B)
  val buf_data = WireInit(0.U(64.W)) // [63:0]
  val buf_tag = WireInit(0.U(TAG.W)) // [TAG-1:0]
  //Miscellaneous signals
  //  val buf_rst = WireInit(Bool(), init = false.B)
  val buf_tag_in = WireInit(0.U(TAG.W)) // [TAG-1:0]
  val buf_addr_in = WireInit(0.U(32.W)) // [31:0]
  val buf_byteen_in = WireInit(0.U(8.W)) // [7:0]
  val buf_data_in = WireInit(0.U(64.W)) // [63:0]
  val buf_write_in = WireInit(Bool(), init = false.B)
  val buf_aligned_in = WireInit(Bool(), init = false.B)
  val buf_size_in = WireInit(0.U(3.W)) // [2:0]

  // val buf_state_en = WireInit(Bool(), init = false.B)
  val buf_wr_en = WireInit(Bool(), init = false.B)
  val buf_data_wr_en = WireInit(Bool(), init = false.B)
  val slvbuf_error_en = WireInit(Bool(), init = false.B)
  val wr_cmd_vld = WireInit(Bool(), init = false.B)

  val cmd_done_rst = WireInit(Bool(), init = false.B)
  val cmd_done = WireInit(Bool(), init = false.B)
  val cmd_doneQ = WireInit(Bool(), init = false.B)
  val trxn_done = WireInit(Bool(), init = false.B)
  val buf_cmd_byte_ptr = WireInit(0.U(3.W)) // [2:0]
  val buf_cmd_byte_ptrQ = WireInit(0.U(3.W)) // [2:0]
  val buf_cmd_nxtbyte_ptr = WireInit(0.U(3.W)) // [2:0]
  val buf_cmd_byte_ptr_en = WireInit(Bool(), init = false.B)
  val found = WireInit(Bool(), init = false.B)

  val slave_valid_pre = WireInit(Bool(), init = false.B)
  val ahb_hready_q = WireInit(Bool(), init = false.B)
  val ahb_hresp_q = WireInit(Bool(), init = false.B)
  val ahb_htrans_q = WireInit(0.U(2.W)) // [1:0]
  val ahb_hwrite_q = WireInit(Bool(), init = false.B)
  val ahb_hrdata_q = WireInit(0.U(64.W)) // [63:0]

  val slvbuf_write = WireInit(Bool(), init = false.B)
  val slvbuf_error = WireInit(Bool(), init = false.B)
  val slvbuf_tag = WireInit(0.U(TAG.W)) // [TAG-1:0]

  val slvbuf_error_in = WireInit(Bool(), init = false.B)
  val slvbuf_wr_en = WireInit(Bool(), init = false.B)
  val bypass_en = WireInit(Bool(), init = false.B)
  val rd_bypass_idle = WireInit(Bool(), init = false.B)

  val last_addr_en = WireInit(Bool(), init = false.B)
  val last_bus_addr = WireInit(0.U(32.W)) // [31:0]
  // Clocks
  val buf_clken = WireInit(Bool(), init = false.B)
  val ahbm_data_clken = WireInit(Bool(), init = false.B)
  val buf_clk = Wire(Clock())
  def get_write_size(byteen: UInt) = {
    val size = ("b11".U & Fill(2, (byteen(7, 0) === "hff".U))) |
      ("b10".U & (Fill(2, ((byteen(7, 0) === "hf0".U) | (byteen(7, 0) === "h0f".U(8.W)))))) |
      ("b01".U(2.W) & (Fill(2, ((byteen(7, 0) === "hc0".U) | (byteen(7, 0) === "h30".U) | (byteen(7, 0) === "h0c".U(8.W)) | (byteen(7, 0) === "h03".U(8.W))))))
    size
  }

  def get_write_addr(byteen_e: UInt) = {
    val addr = ("h0".U(3.W) & (Fill(3, ((byteen_e(7, 0) === "hff".U) | (byteen_e(7, 0) === "h0f".U(8.W)) | (byteen_e(7, 0) === "h03".U(8.W)))))) |
      ("h2".U & (Fill(3, (byteen_e(7, 0) === "h0c".U(8.W))))) |
      ("h4".U & (Fill(3, ((byteen_e(7, 0) === "hf0".U) | (byteen_e(7, 0) === "h03".U(8.W)))))) |
      ("h6".U & (Fill(3, (byteen_e(7, 0) === "hc0".U))))
    ("h6".U & (Fill(3, (byteen_e(7, 0) === "hc0".U))))
    addr
  }

  def get_nxtbyte_ptr(current_byte_ptr: UInt, byteen: UInt, get_next: Bool): UInt = {
    val start_ptr = Mux(get_next, current_byte_ptr + 1.U, current_byte_ptr)
    val temp = (0 until 8).map(j => (byteen(j) & (j.asUInt() >= start_ptr)) -> j.U)
    MuxCase(7.U, temp)
  }
  wr_cmd_vld := wrbuf_vld & wrbuf_data_vld
  master_valid := wr_cmd_vld | io.axi.ar.valid
  master_tag := Mux(wr_cmd_vld.asBool(), wrbuf_tag(TAG - 1, 0), io.axi.ar.bits.id(TAG - 1, 0))
  master_opc := Mux(wr_cmd_vld.asBool(), "b011".U, "b0".U)
  master_addr := Mux(wr_cmd_vld.asBool(), wrbuf_addr(31, 0), io.axi.ar.bits.addr(31, 0))
  master_size := Mux(wr_cmd_vld.asBool(), wrbuf_size(2, 0), io.axi.ar.bits.size(2, 0))
  master_byteen := wrbuf_byteen(7, 0)
  master_wdata := wrbuf_data(63, 0)

  // AXI response channel signals
  io.axi.b.valid := slave_valid & slave_ready & slave_opc(3)
  io.axi.b.bits.resp := Mux(slave_opc(0), "b10".U, Mux(slave_opc(1), "b11".U, "b0".U))
  io.axi.b.bits.id := slave_tag(TAG - 1, 0)

  io.axi.r.valid := slave_valid & slave_ready & (slave_opc(3, 2) === "b0".U)
  io.axi.r.bits.resp := Mux(slave_opc(0), "b10".U, Mux(slave_opc(1), "b11".U, "b0".U))
  io.axi.r.bits.id := slave_tag(TAG - 1, 0)
  io.axi.r.bits.data := slave_rdata(63, 0)
  slave_ready := io.axi.b.ready & io.axi.r.ready

  switch(buf_state) {
    is(idle) {
      master_ready := 1.U
      buf_write_in := (master_opc(2, 1) === "b01".U)
      buf_nxtstate := Mux(buf_write_in.asBool(), cmd_wr, cmd_rd)
      buf_state_en := master_valid & 1.U
      buf_wr_en := buf_state_en
      buf_data_wr_en := buf_state_en & (buf_nxtstate === cmd_wr)
      buf_cmd_byte_ptr_en := buf_state_en
      buf_cmd_byte_ptr := Mux(buf_write_in.asBool(), (get_nxtbyte_ptr(0.U(3.W), buf_byteen_in(7, 0), false.B)), master_addr(2, 0))
      bypass_en := buf_state_en
      rd_bypass_idle := bypass_en & (buf_nxtstate === cmd_rd)
      io.ahb.out.htrans := (Fill(2, bypass_en)) & "b10".U
    }

    is(cmd_rd) {
      buf_nxtstate := Mux((master_valid & (master_opc(2, 0) === "b000".U)).asBool(), stream_rd, data_rd)
      buf_state_en := ahb_hready_q & (ahb_htrans_q(1, 0) =/= "b0".U) & !ahb_hwrite_q
      cmd_done := buf_state_en & !master_valid
      slvbuf_wr_en := buf_state_en
      master_ready := (ahb_hready_q & (ahb_htrans_q(1, 0) =/= "b0".U) & !ahb_hwrite_q) & (Mux((master_valid & (master_opc(2, 0) === "b000".U)).asBool(), stream_rd, data_rd) === stream_rd) ////////////TBD////////
      buf_wr_en := master_ready
      bypass_en := master_ready & master_valid
      buf_cmd_byte_ptr := Mux(bypass_en.asBool(), master_addr(2, 0), buf_addr(2, 0))
      io.ahb.out.htrans := "b10".U & (Fill(2, (!buf_state_en | bypass_en)))
    }

    is(stream_rd) {
      master_ready := (ahb_hready_q & !ahb_hresp_q) & ~(master_valid & master_opc(2, 1) === "b01".U)
      buf_wr_en := (master_valid & master_ready & (master_opc(2, 0) === "b000".U)) // update the fifo if we are streaming the read commands
      buf_nxtstate := Mux(ahb_hresp_q.asBool(), stream_err_rd, Mux((master_valid & master_ready & (master_opc(2, 0) === "b000".U)).asBool(), stream_rd, data_rd)) // assuming that the master accpets the slave response right away.
      buf_state_en := (ahb_hready_q | ahb_hresp_q)
      buf_data_wr_en := buf_state_en
      slvbuf_error_in := ahb_hresp_q
      slvbuf_error_en := buf_state_en
      slave_valid_pre := buf_state_en & !ahb_hresp_q // send a response right away if we are not going through an error response.
      cmd_done := buf_state_en & !master_valid // last one of the stream should not send a htrans
      bypass_en := master_ready & master_valid & (buf_nxtstate === stream_rd) & buf_state_en
      buf_cmd_byte_ptr := Mux(bypass_en.asBool(), master_addr(2, 0), buf_addr(2, 0))
      io.ahb.out.htrans := "b10".U & Fill(2, (!((buf_nxtstate =/= stream_rd) & buf_state_en)))
      slvbuf_wr_en := buf_wr_en // shifting the contents from the buf to slv_buf for streaming cases
    }

    is(stream_err_rd) {
      buf_nxtstate := data_rd
      buf_state_en := ahb_hready_q & (ahb_htrans_q(1, 0) =/= "b0".U) & !ahb_hwrite_q
      slave_valid_pre := buf_state_en
      slvbuf_wr_en := buf_state_en // Overwrite slvbuf with buffer
      buf_cmd_byte_ptr := buf_addr(2, 0)
      io.ahb.out.htrans := "b10".U(2.W) & Fill(2, !buf_state_en)
    }

    is(data_rd) {
      buf_nxtstate := done
      buf_state_en := (ahb_hready_q | ahb_hresp_q)
      buf_data_wr_en := buf_state_en
      slvbuf_error_in := ahb_hresp_q
      slvbuf_error_en := buf_state_en
      slvbuf_wr_en := buf_state_en
    }

    is(cmd_wr) {
      buf_nxtstate := data_wr
      trxn_done := ahb_hready_q & ahb_hwrite_q & (ahb_htrans_q(1, 0) =/= "b0".U)
      buf_state_en := trxn_done
      buf_cmd_byte_ptr_en := buf_state_en
      slvbuf_wr_en := buf_state_en
      buf_cmd_byte_ptr := Mux(trxn_done.asBool(), (get_nxtbyte_ptr(buf_cmd_byte_ptrQ(2, 0), buf_byteen(7, 0), true.B)), buf_cmd_byte_ptrQ)
      cmd_done := trxn_done & (buf_aligned | (buf_cmd_byte_ptrQ === "b111".U) | (buf_byteen((get_nxtbyte_ptr(buf_cmd_byte_ptrQ(2, 0), buf_byteen(7, 0), true.B))) === "b0".U))
      io.ahb.out.htrans := Fill(2, !(cmd_done | cmd_doneQ)) & "b10".U
    }

    is(data_wr) {
      buf_state_en := (cmd_doneQ & ahb_hready_q) | ahb_hresp_q
      master_ready := buf_state_en & !ahb_hresp_q & slave_ready
      buf_nxtstate := Mux((ahb_hresp_q | !slave_ready), done, Mux((master_valid & master_valid), Mux((master_opc(2, 1) === 1.U).asBool(), cmd_wr, cmd_rd), idle))
      slvbuf_error_in := ahb_hresp_q
      slvbuf_error_en := buf_state_en
      buf_write_in := master_opc(2, 1) === 1.U
      buf_wr_en := buf_state_en & ((buf_nxtstate === cmd_wr) | (buf_nxtstate === cmd_rd))
      buf_data_wr_en := buf_wr_en
      cmd_done := (ahb_hresp_q | (ahb_hready_q & (ahb_htrans_q(1, 0) =/= 0.U) &
        ((buf_cmd_byte_ptrQ === 7.U) | (buf_byteen(get_nxtbyte_ptr(buf_cmd_byte_ptrQ(2, 0), buf_byteen(7, 0), true.B)) === 0.U))))
      bypass_en := buf_state_en & buf_write_in & (buf_nxtstate === cmd_wr)
      io.ahb.out.htrans := Fill(2, (!(cmd_done | cmd_doneQ) | bypass_en)) & 2.U
      slave_valid_pre := buf_state_en & (buf_nxtstate =/= done)
      trxn_done := ahb_hready_q & ahb_hwrite_q & (ahb_htrans_q(1, 0) =/= 0.U)
      buf_cmd_byte_ptr_en := trxn_done | bypass_en
      buf_cmd_byte_ptr := Mux(bypass_en, get_nxtbyte_ptr(0.U(3.W), buf_byteen_in(7, 0), false.B), Mux(trxn_done, get_nxtbyte_ptr(buf_cmd_byte_ptrQ(2, 0), buf_byteen(7, 0), true.B), buf_cmd_byte_ptrQ))
    }
    is(done) {
      buf_nxtstate := idle
      buf_state_en := slave_ready
      slvbuf_error_en := true.B
      slave_valid_pre := true.B
    }
  }
  // buf_rst := 0.U
  cmd_done_rst := slave_valid_pre
  buf_addr_in := Cat(master_addr(31, 3), Mux((buf_aligned_in & (master_opc(2, 1) === "b01".U)).asBool(), get_write_addr(master_byteen(7, 0)), master_addr(2, 0)))
  buf_tag_in := master_tag(TAG - 1, 0)
  buf_byteen_in := wrbuf_byteen(7, 0)
  buf_data_in := Mux((buf_state === data_rd), ahb_hrdata_q(63, 0), master_wdata(63, 0))
  buf_size_in := Mux((buf_aligned_in & (master_size(1, 0) === "b11".U) & (master_opc(2, 1) === "b01".U)).asBool(), get_write_size(master_byteen(7, 0)), master_size(1, 0))
  buf_aligned_in := (master_opc(2, 0) === 0.U) | // reads are always aligned since they are either DW or sideeffects
    (master_size(1, 0) === 0.U) | (master_size(1, 0) === "b01".U(2.W)) | (master_size(1, 0) === "b10".U) | // Always aligned for Byte/HW/Word since they can be only for non-idempotent. IFU/SB are always aligned
    ((master_size(1, 0) === "b11".U) & ((master_byteen(7, 0) === "h3".U) | (master_byteen(7, 0) === "hc".U) | (master_byteen(7, 0) === "h30".U) | (master_byteen(7, 0) === "hc0".U) |
      (master_byteen(7, 0) === "hf".U) | (master_byteen(7, 0) === "hf0".U) | (master_byteen(7, 0) === "hff".U)))
  // Generate the ahb signals
  io.ahb.out.haddr := Cat(Mux(bypass_en.asBool(), master_addr(31, 3), buf_addr(31, 3)), Fill(3, io.ahb.out.htrans === 2.U) & buf_cmd_byte_ptr)
  io.ahb.out.hsize := Mux(bypass_en.asBool(), Cat(0.U, (Fill(2, buf_aligned_in) & buf_size_in(1, 0))), Cat("b0".U, (Fill(2, buf_aligned) & buf_size(1, 0))))

  io.ahb.out.hburst := "b0".U
  io.ahb.out.hmastlock := "b0".U
  io.ahb.out.hprot := Cat("b001".U, !io.axi.ar.bits.prot(2))
  io.ahb.out.hwrite := Mux(bypass_en.asBool(), (master_opc(2, 1) === "b01".U), buf_write)
  io.ahb.out.hwdata := buf_data(63, 0)

  slave_valid := slave_valid_pre
  slave_opc := Cat(Mux(slvbuf_write.asBool(), "b11".U, "b00".U), Fill(2, slvbuf_error) & "b10".U)
  slave_rdata := Mux(slvbuf_error.asBool(), Fill(2, last_bus_addr(31, 0)), Mux((buf_state === done), buf_data(63, 0), ahb_hrdata_q(63, 0)))
  slave_tag := slvbuf_tag(TAG - 1, 0)

  last_addr_en := (io.ahb.out.htrans(1, 0) =/= "b0".U) & io.ahb.in.hready & io.ahb.out.hwrite
  // Write buffer
  wrbuf_en := io.axi.aw.valid & io.axi.aw.ready & master_ready
  wrbuf_data_en := io.axi.w.valid & io.axi.w.ready & master_ready
  wrbuf_cmd_sent := master_valid & master_ready & (master_opc(2, 1) === "b01".U)
  wrbuf_rst := (wrbuf_cmd_sent & !wrbuf_en) | dec_tlu_force_halt_bus

  io.axi.aw.ready := !(wrbuf_vld & !wrbuf_cmd_sent) & master_ready
  io.axi.w.ready := !(wrbuf_data_vld & !wrbuf_cmd_sent) & master_ready
  io.axi.ar.ready := !(wrbuf_vld & wrbuf_data_vld) & master_ready
  io.axi.r.bits.last := true.B

  wrbuf_vld := rvdffsc_fpga(1.U, wrbuf_en.asBool(), wrbuf_rst, bus_clk, io.bus_clk_en, clock)
  wrbuf_data_vld := rvdffsc_fpga(1.U, wrbuf_data_en.asBool(), wrbuf_rst, bus_clk, io.bus_clk_en, clock)
  wrbuf_tag := rvdffs_fpga(io.axi.aw.bits.id(TAG - 1, 0), wrbuf_en.asBool(), bus_clk, io.bus_clk_en, clock)
  wrbuf_size := rvdffs_fpga(io.axi.aw.bits.size(2, 0), wrbuf_en.asBool(), bus_clk, io.bus_clk_en, clock)
  wrbuf_addr := rvdffe(io.axi.aw.bits.addr, wrbuf_en.asBool & io.bus_clk_en, clock, io.scan_mode)
  wrbuf_data := rvdffe(io.axi.w.bits.data, wrbuf_data_en.asBool & io.bus_clk_en, clock, io.scan_mode)
  wrbuf_byteen := rvdffs_fpga(io.axi.w.bits.strb(7, 0), wrbuf_data_en.asBool(), bus_clk, io.bus_clk_en, clock)
  last_bus_addr := rvdffs_fpga(io.ahb.out.haddr(31, 0), last_addr_en.asBool(), bus_clk, io.bus_clk_en, clock)
  buf_write         := rvdffs_fpga(buf_write_in, buf_wr_en.asBool(), buf_clk, buf_clken, clock)
  buf_tag           := rvdffs_fpga(buf_tag_in(TAG - 1, 0), buf_wr_en.asBool(), buf_clk, buf_clken, clock)
  buf_addr          := rvdffe(buf_addr_in(31, 0), (buf_wr_en & io.bus_clk_en).asBool, clock, io.scan_mode)
  buf_size          := rvdffs_fpga(buf_size_in(1,0), buf_wr_en.asBool(), buf_clk, buf_clken, clock)
  buf_aligned       := rvdffs_fpga(buf_aligned_in, buf_wr_en.asBool(), buf_clk, buf_clken, clock)
  buf_byteen        := rvdffs_fpga(buf_byteen_in(7, 0), buf_wr_en.asBool(), buf_clk, buf_clken, clock)
  buf_data          := rvdffe(buf_data_in(63, 0), (buf_data_wr_en & io.bus_clk_en).asBool(), clock, io.scan_mode)
  slvbuf_write      := rvdffs_fpga(buf_write, slvbuf_wr_en.asBool(), buf_clk, buf_clken, clock)
  slvbuf_tag        := rvdffs_fpga(buf_tag(TAG - 1, 0), slvbuf_wr_en.asBool(), buf_clk, buf_clken, clock)
  slvbuf_error      := rvdffs_fpga(slvbuf_error_in, slvbuf_error_en.asBool(), bus_clk, io.bus_clk_en, clock)
  cmd_doneQ         := rvdffsc_fpga(1.U, cmd_done.asBool(), cmd_done_rst, bus_clk, io.bus_clk_en, clock)
  buf_cmd_byte_ptrQ := rvdffs_fpga(buf_cmd_byte_ptr(2, 0), buf_cmd_byte_ptr_en.asBool(), bus_clk, io.bus_clk_en, clock)
  ahb_hready_q      := rvdff_fpga(io.ahb.in.hready, bus_clk, io.bus_clk_en, clock)
  ahb_htrans_q      := rvdff_fpga(io.ahb.out.htrans(1, 0), bus_clk,io.bus_clk_en, clock)
  ahb_hwrite_q      := rvdff_fpga(io.ahb.out.hwrite,bus_clk, io.bus_clk_en, clock)
  ahb_hresp_q       := rvdff_fpga(io.ahb.in.hresp,bus_clk, io.bus_clk_en, clock)
  ahb_hrdata_q      := rvdff_fpga(io.ahb.in.hrdata(63, 0), ahbm_data_clk, ahbm_data_clken, clock)
  buf_clken         := io.bus_clk_en & (buf_wr_en | slvbuf_wr_en | io.clk_override)
  ahbm_data_clken   := io.bus_clk_en & ((buf_state =/= idle) | io.clk_override)
  if (RV_FPGA_OPTIMIZE) {
    bus_clk := 0.B.asClock()
    buf_clk := 0.B.asClock()
    ahbm_data_clk := 0.B.asClock()
  }
  else {
    buf_clk := rvclkhdr(clock, buf_clken, io.scan_mode)
    bus_clk := rvclkhdr(clock, io.bus_clk_en, io.scan_mode)
    ahbm_data_clk := rvclkhdr(clock, ahbm_data_clken, io.scan_mode)
  }
}

object axi4_to_ahb extends App {
  println((new chisel3.stage.ChiselStage).emitVerilog(new axi4_to_ahb(1)))
}

dec_tlu_force_halt_bus_q := withClock(io.free_clk) {RegNext(dec_tlu_force_halt_bus_ns, 0.U)}


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

@chiselName
class lsu_stbuf extends Module with lib with RequireAsyncReset {
  val io = IO (new Bundle {
    val lsu_stbuf_c1_clk       	  = Input(Clock())
    val lsu_free_c2_clk        	  = Input(Clock())
    val lsu_pkt_m           	    = Flipped(Valid(new lsu_pkt_t))
    val lsu_pkt_r           	    = Flipped(Valid(new lsu_pkt_t))
    val store_stbuf_reqvld_r	    = Input(Bool())
    val lsu_commit_r              = Input(Bool())
    val dec_lsu_valid_raw_d       = Input(Bool())
    val store_data_hi_r	   	  	  = Input(UInt(DCCM_DATA_WIDTH.W))
    val store_data_lo_r	   	  	  = Input(UInt(DCCM_DATA_WIDTH.W))
    val store_datafn_hi_r         = Input(UInt(DCCM_DATA_WIDTH.W))
    val store_datafn_lo_r	  	    = Input(UInt(DCCM_DATA_WIDTH.W))
    val lsu_stbuf_commit_any	  	= Input(Bool())
    val lsu_addr_d	   	  		    = Input(UInt(LSU_SB_BITS.W))
    val lsu_addr_m	   	  		    = Input(UInt(32.W))
    val lsu_addr_r	   	  		    = Input(UInt(32.W))
    val end_addr_d	   	  		    = Input(UInt(LSU_SB_BITS.W))
    val end_addr_m	   	  		    = Input(UInt(32.W))
    val end_addr_r	   	  		    = Input(UInt(32.W))
    val ldst_dual_d         = Input(Bool())
    val ldst_dual_m         = Input(Bool())
    val ldst_dual_r         = Input(Bool())
    val addr_in_dccm_m            = Input(Bool())
    val addr_in_dccm_r   	  	    = Input(Bool())
    val lsu_cmpen_m   	          = Input(Bool())
    val scan_mode                 = Input(Bool())

    //Outputs
    val stbuf_reqvld_any  	  	  = Output(Bool())
    val stbuf_reqvld_flushed_any  = Output(Bool())
    val stbuf_addr_any   	  	    = Output(UInt(LSU_SB_BITS.W))
    val stbuf_data_any   	  	    = Output(UInt(DCCM_DATA_WIDTH.W))
    val lsu_stbuf_full_any   	    = Output(Bool())
    val lsu_stbuf_empty_any   	  = Output(Bool())
    val ldst_stbuf_reqvld_r   	  = Output(Bool())
    val stbuf_fwddata_hi_m   	    = Output(UInt(DCCM_DATA_WIDTH.W))
    val stbuf_fwddata_lo_m   	    = Output(UInt(DCCM_DATA_WIDTH.W))
    val stbuf_fwdbyteen_hi_m   	  = Output(UInt(DCCM_BYTE_WIDTH.W))
    val stbuf_fwdbyteen_lo_m   	  = Output(UInt(DCCM_BYTE_WIDTH.W))
  })

  io.stbuf_reqvld_any 			      := 0.U
  io.stbuf_reqvld_flushed_any     := 0.U
  io.stbuf_addr_any   	          := 0.U
  io.stbuf_data_any   	          := 0.U
  io.lsu_stbuf_full_any   	  	  := 0.U
  io.lsu_stbuf_empty_any   	  	  := 0.U
  io.ldst_stbuf_reqvld_r   	  	  := 0.U
  io.stbuf_fwddata_hi_m   	  	  := 0.U
  io.stbuf_fwddata_lo_m   	  	  := 0.U
  io.stbuf_fwdbyteen_hi_m   	    := 0.U
  io.stbuf_fwdbyteen_lo_m   	    := 0.U

  val stbuf_vld                = WireInit(UInt(LSU_STBUF_DEPTH.W), init = 0.U)
  val stbuf_wr_en               = WireInit(UInt(LSU_STBUF_DEPTH.W), init = 0.U)
  val stbuf_dma_kill_en        = WireInit(UInt(LSU_STBUF_DEPTH.W), init = 0.U)
  val stbuf_dma_kill           = WireInit(UInt(LSU_STBUF_DEPTH.W), init = 0.U)
  val stbuf_reset              = WireInit(UInt(LSU_STBUF_DEPTH.W), init = 0.U)
  val store_byteen_ext_r       = WireInit(UInt(8.W), init= 0.U)
  val stbuf_addr               = Wire(Vec(LSU_STBUF_DEPTH,UInt(LSU_SB_BITS.W)))
  stbuf_addr := (0 until LSU_STBUF_DEPTH).map(i => 0.U)
  val stbuf_byteen             = Wire(Vec(LSU_STBUF_DEPTH,UInt(DCCM_BYTE_WIDTH.W)))
  stbuf_byteen := (0 until LSU_STBUF_DEPTH).map(i => 0.U)
  val stbuf_data               = Wire(Vec(LSU_STBUF_DEPTH,UInt(DCCM_DATA_WIDTH.W)))
  stbuf_data := (0 until LSU_STBUF_DEPTH).map(i => 0.U)
  val stbuf_addrin             = Wire(Vec(LSU_STBUF_DEPTH,UInt(LSU_SB_BITS.W)))
  stbuf_addrin := (0 until LSU_STBUF_DEPTH).map(i => 0.U)
  val stbuf_datain             = Wire(Vec(LSU_STBUF_DEPTH,UInt(DCCM_DATA_WIDTH.W)))
  stbuf_datain := (0 until LSU_STBUF_DEPTH).map(i => 0.U)
  val stbuf_byteenin           = Wire(Vec(LSU_STBUF_DEPTH,UInt(DCCM_BYTE_WIDTH.W)))
  stbuf_byteenin := (0 until LSU_STBUF_DEPTH).map(i => 0.U)
  val WrPtr                    = WireInit(UInt(log2Ceil(LSU_STBUF_DEPTH).W),init = 0.U)
  val RdPtr                    = WireInit(UInt(log2Ceil(LSU_STBUF_DEPTH).W),init = 0.U)
  val cmpaddr_hi_m             = WireInit(0.U(16.W))
  val stbuf_specvld_m          = WireInit(0.U(2.W))
  val stbuf_specvld_r          = WireInit(0.U(2.W))
  val cmpaddr_lo_m             = WireInit(0.U(16.W))
  val stbuf_fwdata_hi_pre_m	= WireInit(UInt(DCCM_DATA_WIDTH.W),init = 0.U)
  val stbuf_fwdata_lo_pre_m	= WireInit(UInt(DCCM_DATA_WIDTH.W),init = 0.U)
  val ld_byte_rhit_lo_lo	  	= WireInit(UInt(DCCM_BYTE_WIDTH.W),init = 0.U)
  val ld_byte_rhit_hi_lo	  	= WireInit(UInt(DCCM_BYTE_WIDTH.W),init = 0.U)
  val ld_byte_rhit_lo_hi	  	= WireInit(UInt(DCCM_BYTE_WIDTH.W),init = 0.U)
  val ld_byte_rhit_hi_hi	  	= WireInit(UInt(DCCM_BYTE_WIDTH.W),init = 0.U)
  val ld_byte_hit_lo	        = WireInit(UInt(DCCM_BYTE_WIDTH.W),init = 0.U)
  val ld_byte_rhit_lo	        = WireInit(UInt(DCCM_BYTE_WIDTH.W),init = 0.U)
  val ld_byte_hit_hi	        = WireInit(UInt(DCCM_BYTE_WIDTH.W),init = 0.U)
  val ld_byte_rhit_hi	        = WireInit(UInt(DCCM_BYTE_WIDTH.W),init = 0.U)
  val ldst_byteen_ext_r	  	= WireInit(UInt(8.W),init = 0.U)
  val ld_fwddata_rpipe_lo	  	= WireInit(UInt(32.W),init = 0.U)
  val ld_fwddata_rpipe_hi	  	= WireInit(UInt(32.W),init = 0.U)

  //
  val datain1             = Wire(Vec(LSU_STBUF_DEPTH,UInt(8.W)))
  val datain2             = Wire(Vec(LSU_STBUF_DEPTH,UInt(8.W)))
  val datain3             = Wire(Vec(LSU_STBUF_DEPTH,UInt(8.W)))
  val datain4             = Wire(Vec(LSU_STBUF_DEPTH,UInt(8.W)))

  //////////////////////////////////////Code Start here///////////////////////////////
  val ldst_byteen_r = Mux1H(Seq(
    io.lsu_pkt_r.bits.by.asBool -> "b00000001".U,
    io.lsu_pkt_r.bits.half.asBool ->"b00000011".U,
    io.lsu_pkt_r.bits.word.asBool -> "b00001111".U,
    io.lsu_pkt_r.bits.dword.asBool -> "b11111111".U
  ))
  val dual_stbuf_write_r = io.ldst_dual_r & io.store_stbuf_reqvld_r

  store_byteen_ext_r := ldst_byteen_r << io.lsu_addr_r(1,0)
  val store_byteen_hi_r = store_byteen_ext_r (7,4) & Fill(4, io.lsu_pkt_r.bits.store)
  val store_byteen_lo_r = store_byteen_ext_r (3,0) & Fill(4, io.lsu_pkt_r.bits.store)

  val RdPtrPlus1 = RdPtr + "b01".U
  val WrPtrPlus1 = WrPtr + "b01".U
  val WrPtrPlus2 = WrPtr + "b10".U

  io.ldst_stbuf_reqvld_r := (io.lsu_commit_r | io.lsu_pkt_r.bits.dma) & io.store_stbuf_reqvld_r

  val store_matchvec_lo_r =  (0 until LSU_STBUF_DEPTH).map(i=> (stbuf_addr(i)(LSU_SB_BITS-1,log2Ceil(DCCM_BYTE_WIDTH)) === io.lsu_addr_r(LSU_SB_BITS-1,log2Ceil(DCCM_BYTE_WIDTH)) &  stbuf_vld(i) & !stbuf_dma_kill(i) & !stbuf_reset(i)).asUInt).reverse.reduce(Cat(_,_))
  val store_matchvec_hi_r =  (0 until LSU_STBUF_DEPTH).map(i=> (stbuf_addr(i)(LSU_SB_BITS-1,log2Ceil(DCCM_BYTE_WIDTH)) === io.end_addr_r(LSU_SB_BITS-1,log2Ceil(DCCM_BYTE_WIDTH)) & stbuf_vld(i) & !stbuf_dma_kill(i) & dual_stbuf_write_r & !stbuf_reset(i)).asUInt).reverse.reduce(Cat(_,_))

  val store_coalesce_lo_r = store_matchvec_lo_r.orR
  val store_coalesce_hi_r = store_matchvec_hi_r.orR
  if (DCCM_ENABLE == 1) {
    stbuf_wr_en := (0 until LSU_STBUF_DEPTH).map(i => (io.ldst_stbuf_reqvld_r & (
      ((i.asUInt === WrPtr) & !store_coalesce_lo_r) |
        ((i.asUInt === WrPtr) & dual_stbuf_write_r & !store_coalesce_hi_r) |
        ((i.asUInt === WrPtrPlus1) & dual_stbuf_write_r & !(store_coalesce_lo_r | store_coalesce_hi_r)) |
        store_matchvec_lo_r(i) | store_matchvec_hi_r(i))).asUInt).reverse.reduce(Cat(_, _))
    stbuf_reset := (0 until LSU_STBUF_DEPTH).map(i => ((io.lsu_stbuf_commit_any | io.stbuf_reqvld_flushed_any) & (i.asUInt === RdPtr).asBool).asUInt).reverse.reduce(Cat(_, _))
    val sel_lo = (0 until LSU_STBUF_DEPTH).map(i => (((!io.ldst_dual_r | io.store_stbuf_reqvld_r) & (i.asUInt === WrPtr).asBool & !store_coalesce_lo_r) | store_matchvec_lo_r(i)).asUInt).reverse.reduce(Cat(_, _))

    stbuf_addrin := (0 until LSU_STBUF_DEPTH).map(i => Mux(sel_lo(i), io.lsu_addr_r(LSU_SB_BITS - 1, 0), io.end_addr_r(LSU_SB_BITS - 1, 0)))
    stbuf_byteenin := (0 until LSU_STBUF_DEPTH).map(i => Mux(sel_lo(i), stbuf_byteen(i) | store_byteen_lo_r, stbuf_byteen(i) | store_byteen_hi_r).asUInt)

    datain1 := (0 until LSU_STBUF_DEPTH).map(i => Mux(sel_lo(i), Mux(!stbuf_byteen(i)(0) | store_byteen_lo_r(0), io.store_datafn_lo_r(7, 0), stbuf_data(i)(7, 0)),
      Mux(!stbuf_byteen(i)(0) | store_byteen_hi_r(0), io.store_datafn_hi_r(7, 0), stbuf_data(i)(7, 0))).asUInt)

    datain2 := (0 until LSU_STBUF_DEPTH).map(i => Mux(sel_lo(i), Mux(!stbuf_byteen(i)(1) | store_byteen_lo_r(1), io.store_datafn_lo_r(15, 8), stbuf_data(i)(15, 8)),
      Mux(!stbuf_byteen(i)(1) | store_byteen_hi_r(1), io.store_datafn_hi_r(15, 8), stbuf_data(i)(15, 8))).asUInt)

    datain3 := (0 until LSU_STBUF_DEPTH).map(i => Mux(sel_lo(i), Mux(!stbuf_byteen(i)(2) | store_byteen_lo_r(2), io.store_datafn_lo_r(23, 16), stbuf_data(i)(23, 16)),
      Mux(!stbuf_byteen(i)(2) | store_byteen_hi_r(2), io.store_datafn_hi_r(23, 16), stbuf_data(i)(23, 16))).asUInt)

    datain4 := (0 until LSU_STBUF_DEPTH).map(i => Mux(sel_lo(i), Mux(!stbuf_byteen(i)(3) | store_byteen_lo_r(3), io.store_datafn_lo_r(31, 24), stbuf_data(i)(31, 24)),
      Mux(!stbuf_byteen(i)(3) | store_byteen_hi_r(3), io.store_datafn_hi_r(31, 24), stbuf_data(i)(31, 24))).asUInt)

    stbuf_datain := (0 until LSU_STBUF_DEPTH).map(i => Cat(datain4(i), datain3(i), datain2(i), datain1(i)))

    stbuf_vld := (0 until LSU_STBUF_DEPTH).map(i => withClock(io.lsu_free_c2_clk) {
      RegNext(Mux(stbuf_wr_en(i).asBool(), 1.U, stbuf_vld(i)) & !stbuf_reset(i), 0.U)
    }).reverse.reduce(Cat(_, _))
    stbuf_dma_kill := (0 until LSU_STBUF_DEPTH).map(i => withClock(io.lsu_free_c2_clk) {
      RegNext(Mux(stbuf_dma_kill_en(i).asBool, 1.U, stbuf_dma_kill(i)) & !stbuf_reset(i), 0.U)
    }).reverse.reduce(Cat(_, _))
    stbuf_byteen := (0 until LSU_STBUF_DEPTH).map(i => withClock(io.lsu_stbuf_c1_clk) {
      RegNext(Mux(stbuf_wr_en(i).asBool(), stbuf_byteenin(i), stbuf_byteen(i)) & Fill(stbuf_byteenin(i).getWidth, !stbuf_reset(i)), 0.U)
    })
    for (i <- 0 until LSU_STBUF_DEPTH) {
      stbuf_addr(i) := rvdffe(stbuf_addrin(i), stbuf_wr_en(i).asBool(), clock, io.scan_mode)
      stbuf_data(i) := rvdffe(stbuf_datain(i), stbuf_wr_en(i).asBool(), clock, io.scan_mode)
    }
  } else {
    stbuf_wr_en := 0.U
    stbuf_reset := 0.U
    stbuf_vld   := 0.U
    stbuf_dma_kill := 0.U
    stbuf_addr := 0.U
    stbuf_byteen := 0.U
    stbuf_data := 0.U
  }

  // Store Buffer drain logic
  io.stbuf_reqvld_flushed_any := stbuf_vld(RdPtr) & stbuf_dma_kill(RdPtr)
  io.stbuf_reqvld_any  := stbuf_vld(RdPtr) & !stbuf_dma_kill(RdPtr) & !(stbuf_dma_kill_en.orR)
  io.stbuf_addr_any  := stbuf_addr(RdPtr)
  io.stbuf_data_any  := stbuf_data(RdPtr)

  val WrPtrEn = ((io.ldst_stbuf_reqvld_r & !dual_stbuf_write_r & !(store_coalesce_hi_r | store_coalesce_lo_r))  |
    (io.ldst_stbuf_reqvld_r  &  dual_stbuf_write_r & !(store_coalesce_hi_r & store_coalesce_lo_r))).asBool
  val NxtWrPtr = Mux((io.ldst_stbuf_reqvld_r & dual_stbuf_write_r & !(store_coalesce_hi_r | store_coalesce_lo_r)).asBool, WrPtrPlus2, WrPtrPlus1)
  val RdPtrEn  = io.lsu_stbuf_commit_any | io.stbuf_reqvld_flushed_any
  val NxtRdPtr = RdPtrPlus1

  withClock(io.lsu_stbuf_c1_clk){ WrPtr := RegEnable(NxtWrPtr, 0.U, WrPtrEn)}

  val stbuf_numvld_any = VecInit.tabulate(LSU_STBUF_DEPTH)(i=>Cat(0.U(3.W), stbuf_vld(i))).reduce (_+_)
  val isdccmst_m = io.lsu_pkt_m.valid & io.lsu_pkt_m.bits.store & io.addr_in_dccm_m & !io.lsu_pkt_m.bits.dma
  val isdccmst_r = io.lsu_pkt_r.valid & io.lsu_pkt_r.bits.store & io.addr_in_dccm_r & !io.lsu_pkt_r.bits.dma

  stbuf_specvld_m := Cat(0.U(1.W),isdccmst_m) << (isdccmst_m & io.ldst_dual_m)
  stbuf_specvld_r := Cat(0.U(1.W),isdccmst_r) << (isdccmst_r & io.ldst_dual_r)
  val stbuf_specvld_any = stbuf_numvld_any +  Cat(0.U(2.W), stbuf_specvld_m) + Cat(0.U(2.W), stbuf_specvld_r)

  io.lsu_stbuf_full_any  := Mux((!io.ldst_dual_d & io.dec_lsu_valid_raw_d).asBool,(stbuf_specvld_any >= LSU_STBUF_DEPTH.U),(stbuf_specvld_any >= (LSU_STBUF_DEPTH-1).U))
  io.lsu_stbuf_empty_any := stbuf_numvld_any === 0.U

  cmpaddr_hi_m := io.end_addr_m(LSU_SB_BITS-1,log2Ceil(DCCM_BYTE_WIDTH))
  cmpaddr_lo_m  := io.lsu_addr_m(LSU_SB_BITS-1,log2Ceil(DCCM_BYTE_WIDTH))

  val stbuf_match_hi = (0 until LSU_STBUF_DEPTH).map(i=> ((stbuf_addr(i)(LSU_SB_BITS-1,log2Ceil(DCCM_BYTE_WIDTH)) === cmpaddr_hi_m(13,0)) & stbuf_vld(i) & !stbuf_dma_kill(i) & io.addr_in_dccm_m).asUInt).reverse.reduce(Cat(_,_))
  val stbuf_match_lo = (0 until LSU_STBUF_DEPTH).map(i=> ((stbuf_addr(i)(LSU_SB_BITS-1,log2Ceil(DCCM_BYTE_WIDTH)) === cmpaddr_lo_m(13,0)) & stbuf_vld(i) & !stbuf_dma_kill(i) & io.addr_in_dccm_m).asUInt).reverse.reduce(Cat(_,_))
  stbuf_dma_kill_en := (0 until LSU_STBUF_DEPTH).map(i=> ((stbuf_match_hi(i) | stbuf_match_lo(i)) & io.lsu_pkt_m.valid & io.lsu_pkt_m.bits.dma & io.lsu_pkt_m.bits.store).asUInt).reverse.reduce(Cat(_,_))

  val stbuf_fwdbyteenvec_hi = (0 until LSU_STBUF_DEPTH).map(i=>(0 until DCCM_BYTE_WIDTH).map(j=> stbuf_match_hi(i) & stbuf_byteen(i)(j) & stbuf_vld(i).asUInt()))
  val stbuf_fwdbyteenvec_lo = (0 until LSU_STBUF_DEPTH).map(i=>(0 until DCCM_BYTE_WIDTH).map(j=> stbuf_match_lo(i) & stbuf_byteen(i)(j) & stbuf_vld(i).asUInt()))
  val stbuf_fwdbyteen_hi_pre_m = (0 until LSU_STBUF_DEPTH).map(j=>(0 until DCCM_BYTE_WIDTH).map(i=> stbuf_fwdbyteenvec_hi(i)(j).asUInt()).reduce(_|_))
  val stbuf_fwdbyteen_lo_pre_m = (0 until LSU_STBUF_DEPTH).map(j=>(0 until DCCM_BYTE_WIDTH).map(i=> stbuf_fwdbyteenvec_lo(i)(j).asUInt()).reduce(_|_))

  val stbuf_fwddata_hi_pre_m = VecInit.tabulate(LSU_STBUF_DEPTH)(i=> Fill(32,stbuf_match_hi(i)) & stbuf_data(i)).reverse.reduce(_|_)
  val stbuf_fwddata_lo_pre_m = VecInit.tabulate(LSU_STBUF_DEPTH)(i=> Fill(32,stbuf_match_lo(i)) & stbuf_data(i)).reverse.reduce(_|_)

  ldst_byteen_ext_r := ldst_byteen_r << io.lsu_addr_r(1,0)
  val ldst_byteen_hi_r = ldst_byteen_ext_r(7,4)
  val ldst_byteen_lo_r = ldst_byteen_ext_r(3,0)

  val ld_addr_rhit_lo_lo = (io.lsu_addr_m(31,2) === io.lsu_addr_r(31,2)) & io.lsu_pkt_r.valid & io.lsu_pkt_r.bits.store & !io.lsu_pkt_r.bits.dma
  val ld_addr_rhit_lo_hi = (io.end_addr_m(31,2) === io.lsu_addr_r(31,2)) & io.lsu_pkt_r.valid & io.lsu_pkt_r.bits.store & !io.lsu_pkt_r.bits.dma
  val ld_addr_rhit_hi_lo = (io.lsu_addr_m(31,2) === io.end_addr_r(31,2)) & io.lsu_pkt_r.valid & io.lsu_pkt_r.bits.store & !io.lsu_pkt_r.bits.dma & dual_stbuf_write_r
  val ld_addr_rhit_hi_hi = (io.end_addr_m(31,2) === io.end_addr_r(31,2)) & io.lsu_pkt_r.valid & io.lsu_pkt_r.bits.store & !io.lsu_pkt_r.bits.dma & dual_stbuf_write_r

  ld_byte_rhit_lo_lo := (0 until DCCM_BYTE_WIDTH).map(i=> (ld_addr_rhit_lo_lo & ldst_byteen_lo_r(i)).asUInt).reverse.reduce(Cat(_,_))
  ld_byte_rhit_lo_hi := (0 until DCCM_BYTE_WIDTH).map(i=> (ld_addr_rhit_lo_hi & ldst_byteen_lo_r(i)).asUInt).reverse.reduce(Cat(_,_))
  ld_byte_rhit_hi_lo := (0 until DCCM_BYTE_WIDTH).map(i=> (ld_addr_rhit_hi_lo & ldst_byteen_hi_r(i)).asUInt).reverse.reduce(Cat(_,_))
  ld_byte_rhit_hi_hi := (0 until DCCM_BYTE_WIDTH).map(i=> (ld_addr_rhit_hi_hi & ldst_byteen_hi_r(i)).asUInt).reverse.reduce(Cat(_,_))

  ld_byte_rhit_lo := (0 until DCCM_BYTE_WIDTH).map(i=> (ld_byte_rhit_lo_lo(i) | ld_byte_rhit_hi_lo(i)).asUInt).reverse.reduce(Cat(_,_))
  ld_byte_rhit_hi := (0 until DCCM_BYTE_WIDTH).map(i=> (ld_byte_rhit_lo_hi(i) | ld_byte_rhit_hi_hi(i)).asUInt).reverse.reduce(Cat(_,_))

  val fwdpipe1_lo = (Fill(8, ld_byte_rhit_lo_lo(0)) & io.store_data_lo_r(7,0)) | (Fill(8, ld_byte_rhit_hi_lo(0)) & io.store_data_hi_r(7,0))
  val fwdpipe2_lo = (Fill(8, ld_byte_rhit_lo_lo(1)) & io.store_data_lo_r(15,8)) | (Fill(8, ld_byte_rhit_hi_lo(1)) & io.store_data_hi_r(15,8))
  val fwdpipe3_lo = (Fill(8, ld_byte_rhit_lo_lo(2)) & io.store_data_lo_r(23,16)) | (Fill(8, ld_byte_rhit_hi_lo(2)) & io.store_data_hi_r(23,16))
  val fwdpipe4_lo = (Fill(8, ld_byte_rhit_lo_lo(3)) & io.store_data_lo_r(31,24)) | (Fill(8, ld_byte_rhit_hi_lo(3)) & io.store_data_hi_r(31,24))
  ld_fwddata_rpipe_lo := Cat(fwdpipe4_lo,fwdpipe3_lo,fwdpipe2_lo,fwdpipe1_lo)

  val fwdpipe1_hi = (Fill(8, ld_byte_rhit_lo_hi(0)) & io.store_data_lo_r(7,0)) | (Fill(8, ld_byte_rhit_hi_hi(0)) & io.store_data_hi_r(7,0))
  val fwdpipe2_hi = (Fill(8, ld_byte_rhit_lo_hi(1)) & io.store_data_lo_r(15,8)) | (Fill(8, ld_byte_rhit_hi_hi(1)) & io.store_data_hi_r(15,8))
  val fwdpipe3_hi = (Fill(8, ld_byte_rhit_lo_hi(2)) & io.store_data_lo_r(23,16)) | (Fill(8, ld_byte_rhit_hi_hi(2)) & io.store_data_hi_r(23,16))
  val fwdpipe4_hi = (Fill(8, ld_byte_rhit_lo_hi(3)) & io.store_data_lo_r(31,24)) | (Fill(8, ld_byte_rhit_hi_hi(3)) & io.store_data_hi_r(31,24))
  ld_fwddata_rpipe_hi := Cat(fwdpipe4_hi,fwdpipe3_hi,fwdpipe2_hi,fwdpipe1_hi)

  ld_byte_hit_lo := (0 until DCCM_BYTE_WIDTH).map(i=> (ld_byte_rhit_lo_lo(i) | ld_byte_rhit_hi_lo(i)).asUInt).reverse.reduce(Cat(_,_))
  ld_byte_hit_hi := (0 until DCCM_BYTE_WIDTH).map(i=> (ld_byte_rhit_lo_hi(i) | ld_byte_rhit_hi_hi(i)).asUInt).reverse.reduce(Cat(_,_))

  io.stbuf_fwdbyteen_hi_m := (0 until DCCM_BYTE_WIDTH).map(i=> (ld_byte_hit_hi(i) | stbuf_fwdbyteen_hi_pre_m(i)).asUInt).reverse.reduce(Cat(_,_))
  io.stbuf_fwdbyteen_lo_m := (0 until DCCM_BYTE_WIDTH).map(i=> (ld_byte_hit_lo(i) | stbuf_fwdbyteen_lo_pre_m(i)).asUInt).reverse.reduce(Cat(_,_))

  // Pipe vs Store Queue priority
  val stbuf_fwdpipe1_lo = Mux(ld_byte_rhit_lo(0),ld_fwddata_rpipe_lo(7,0),stbuf_fwddata_lo_pre_m(7,0))
  val stbuf_fwdpipe2_lo = Mux(ld_byte_rhit_lo(1),ld_fwddata_rpipe_lo(15,8),stbuf_fwddata_lo_pre_m(15,8))
  val stbuf_fwdpipe3_lo = Mux(ld_byte_rhit_lo(2),ld_fwddata_rpipe_lo(23,16),stbuf_fwddata_lo_pre_m(23,16))
  val stbuf_fwdpipe4_lo = Mux(ld_byte_rhit_lo(3),ld_fwddata_rpipe_lo(31,24),stbuf_fwddata_lo_pre_m(31,24))
  io.stbuf_fwddata_lo_m := Cat(stbuf_fwdpipe4_lo,stbuf_fwdpipe3_lo,stbuf_fwdpipe2_lo,stbuf_fwdpipe1_lo)
  // Pipe vs Store Queue priority
  val stbuf_fwdpipe1_hi = Mux(ld_byte_rhit_hi(0),ld_fwddata_rpipe_hi(7,0),stbuf_fwddata_hi_pre_m(7,0))
  val stbuf_fwdpipe2_hi = Mux(ld_byte_rhit_hi(1),ld_fwddata_rpipe_hi(15,8),stbuf_fwddata_hi_pre_m(15,8))
  val stbuf_fwdpipe3_hi = Mux(ld_byte_rhit_hi(2),ld_fwddata_rpipe_hi(23,16),stbuf_fwddata_hi_pre_m(23,16))
  val stbuf_fwdpipe4_hi = Mux(ld_byte_rhit_hi(3),ld_fwddata_rpipe_hi(31,24),stbuf_fwddata_hi_pre_m(31,24))
  io.stbuf_fwddata_hi_m := Cat(stbuf_fwdpipe4_hi,stbuf_fwdpipe3_hi,stbuf_fwdpipe2_hi,stbuf_fwdpipe1_hi)
}
object stbuf extends App {
  println((new chisel3.stage.ChiselStage).emitVerilog(new lsu_stbuf()))
}

withClock(io.lsu_stbuf_c1_clk){ RdPtr := RegEnable(NxtRdPtr, 0.U, RdPtrEn)}