/***************************************************************************************
* 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.
*
*
* Acknowledgement
*
* This implementation is inspired by several key papers:
* [1] Richard Kessler. "[The alpha 21264 microprocessor.](https://doi.org/10.1109/40.755465)" IEEE Micro 19.2: 24-36.
* 1999.
***************************************************************************************/

package xiangshan.mem.mdp

import org.chipsalliance.cde.config.Parameters
import chisel3._
import chisel3.util._
import xiangshan._
import utils._
import utility._

// 21264-like wait table, uses 2-bit counter
class WaitTable(implicit p: Parameters) extends XSModule {
  val io = IO(new Bundle {
    // to decode
    val raddr = Vec(DecodeWidth, Input(UInt(MemPredPCWidth.W))) // decode pc(VaddrBits-1, 1)
    val rdata = Vec(DecodeWidth, Output(Bool())) // loadWaitBit
    val update = Input(new MemPredUpdateReq) // RegNext should be added outside
    val csrCtrl = Input(new CustomCSRCtrlIO)
  })

  require(DecodeWidth == RenameWidth)

  val data = RegInit(VecInit(Seq.fill(WaitTableSize)(0.U(2.W))))
  val resetCounter = RegInit(0.U(ResetTimeMax2Pow.W))
  resetCounter := resetCounter + 1.U

  // read ports
  for (i <- 0 until DecodeWidth) {
    io.rdata(i) := (data(io.raddr(i))(LWTUse2BitCounter.B.asUInt) || io.csrCtrl.no_spec_load) && !io.csrCtrl.lvpred_disable
  }

  // write port
  when(io.update.valid){
    data(io.update.waddr) := Cat(data(io.update.waddr)(0), true.B)
  }

  // reset period: ResetTimeMax2Pow
  when(resetCounter(ResetTimeMax2Pow-1, ResetTimeMin2Pow)(RegNext(io.csrCtrl.lvpred_timeout))) {
    for (j <- 0 until WaitTableSize) {
      data(j) := 0.U
    }
    resetCounter:= 0.U
  }

  // debug
  XSDebug(io.update.valid, "%d: waittable update: pc %x data: %x\n", GTimer(), io.update.waddr, io.update.wdata)

  XSPerfAccumulate("wait_table_bit_set", PopCount(data.map(d => d(1))))
}
