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

package xiangshan.cache

import org.chipsalliance.cde.config.Parameters
import chisel3._
import chisel3.util._
import utility.XSDebug

class AtomicsReplayEntry(implicit p: Parameters) extends DCacheModule
{
  val io = IO(new Bundle {
    val lsu  = Flipped(new AtomicWordIO)
    val pipe_req  = Decoupled(new MainPipeReq)
    val pipe_resp = Flipped(ValidIO(new MainPipeResp))
    val block_lr = Input(Bool())

    val block_addr  = Output(Valid(UInt()))
  })

  val s_invalid :: s_pipe_req :: s_pipe_resp :: s_resp :: Nil = Enum(4)
  val state = RegInit(s_invalid)

  val req = Reg(new DCacheWordReqWithVaddr)

  // assign default values to output signals
  io.lsu.req.ready     := state === s_invalid
  io.lsu.resp.valid    := false.B
  io.lsu.resp.bits     := DontCare

  io.pipe_req.valid    := false.B
  io.pipe_req.bits     := DontCare

  io.block_addr.valid := state =/= s_invalid
  io.block_addr.bits  := req.addr


  XSDebug(state =/= s_invalid, "AtomicsReplayEntry: state: %d block_addr: %x\n", state, io.block_addr.bits)

  // --------------------------------------------
  // s_invalid: receive requests
  when (state === s_invalid) {
    when (io.lsu.req.fire) {
      req   := io.lsu.req.bits
      state := s_pipe_req
    }
  }

  // --------------------------------------------
  // replay
  when (state === s_pipe_req) {
    io.pipe_req.valid := Mux(
      io.pipe_req.bits.cmd === M_XLR,
      !io.block_lr, // block lr to survive in lr storm
      true.B
    )

    val pipe_req = io.pipe_req.bits
    pipe_req := DontCare
    pipe_req.miss := false.B
    pipe_req.probe := false.B
    pipe_req.probe_need_data := false.B
    pipe_req.source := AMO_SOURCE.U
    pipe_req.cmd    := req.cmd
    pipe_req.addr   := get_block_addr(req.addr)
    pipe_req.vaddr  := get_block_addr(req.vaddr)
    pipe_req.word_idx  := get_word(req.addr)
    pipe_req.amo_data  := req.data
    pipe_req.amo_mask  := req.mask

    when (io.pipe_req.fire) {
      state := s_pipe_resp
      assert(!io.pipe_req.bits.vaddr === 0.U)
    }
  }

  val resp_data  = Reg(UInt())
  val resp_id    = Reg(UInt())
  val resp_error = Reg(Bool())
  when (state === s_pipe_resp) {
    // when not miss
    // everything is OK, simply send response back to sbuffer
    // when miss and not replay
    // wait for missQueue to handling miss and replaying our request
    // when miss and replay
    // req missed and fail to enter missQueue, manually replay it later
    // TODO: add assertions:
    // 1. add a replay delay counter?
    // 2. when req gets into MissQueue, it should not miss any more
    when (io.pipe_resp.fire) {
      when (io.pipe_resp.bits.miss) {
        when (io.pipe_resp.bits.replay) {
          state := s_pipe_req
        }
      } .otherwise {
        resp_data  := io.pipe_resp.bits.data
        resp_id    := io.pipe_resp.bits.id
        resp_error := io.pipe_resp.bits.error
        state := s_resp
      }
    }
  }

  // --------------------------------------------
  when (state === s_resp) {
    io.lsu.resp.valid := true.B
    io.lsu.resp.bits  := DontCare
    io.lsu.resp.bits.data  := resp_data
    io.lsu.resp.bits.id    := resp_id
    io.lsu.resp.bits.error := resp_error

    when (io.lsu.resp.fire) {
      state := s_invalid
    }
  }

  // debug output
  // when (io.lsu.req.fire) {
  //   io.lsu.req.bits.dump()
  // }

  // when (io.lsu.resp.fire) {
  //   io.lsu.resp.bits.dump()
  // }

//  when (io.pipe_req.fire) {
//    io.pipe_req.bits.dump()
//  }
//
//  when (io.pipe_resp.fire) {
//    io.pipe_resp.bits.dump()
//  }
}