// SPDX-License-Identifier: Apache-2.0

package chiseltest.simulator

import firrtl2._
import firrtl2.annotations._
import chiseltest.simulator.jna._
import chiseltest.simulator.Utils.quoteCmdArgs

case object VerilatorBackendAnnotation extends SimulatorAnnotation {
  override def getSimulator: Simulator = VerilatorSimulator
}

/** verilator specific options */
trait VerilatorOption extends NoTargetAnnotation

/** adds flags to the invocation of verilator */
case class VerilatorFlags(flags: Seq[String]) extends VerilatorOption

/** adds flags to the C++ compiler in the Makefile generated by verilator */
case class VerilatorCFlags(flags: Seq[String]) extends VerilatorOption

/** adds flags to the linker in the Makefile generated by verilator */
case class VerilatorLinkFlags(flags: Seq[String]) extends VerilatorOption

private object VerilatorSimulator extends Simulator {
  override def name: String = "verilator"

  /** is this simulator installed on the local machine? */
  override def isAvailable: Boolean = {
    val binaryFound = os.proc("which", "verilator").call().exitCode == 0
    binaryFound && majorVersion >= 4
  }

  override def supportsCoverage = true
  override def supportsLiveCoverage = true
  override def waveformFormats = Seq(WriteVcdAnnotation, WriteFstAnnotation)

  /** search the local computer for an installation of this simulator and print versions */
  def findVersions(): Unit = {
    if (isAvailable) {
      val (maj, min) = version
      println(s"Found Verilator $maj.$min")
    }
  }

  // example version string1: Verilator 4.038 2020-07-11 rev v4.038
  // example version string2: Verilator 5.002.p 2022-11-15 rev v5.002-12-g58821e0eb
  private lazy val version: (Int, Int) = { // (major, minor)
    val versionSplitted = os
      .proc(

        else { "verilator" },
        "--version"
      )
      .call()
      .out
      .trim()
      .split(' ')
    assert(
      versionSplitted.length > 1 && versionSplitted.head == "Verilator",
      s"Unknown verilator version string: ${versionSplitted.mkString(" ")}"
    )

    val (maj, min) = versionSplitted(1).split('.').map(_.trim) match {
      case Array(majStr, minStr) =>
        assert(majStr.length == 1 && minStr.length == 3, s"${majStr}.${minStr} is not of the expected format: D.DDD")
        (majStr.toInt, minStr.toInt)
      case Array(majStr, minStr, s) =>
        assert(majStr.length == 1 && minStr.length == 3, s"${majStr}.${minStr} is not of the expected format: D.DDD.s+")
        (majStr.toInt, minStr.toInt)
      case s =>
        assert(false, s"${s} is not of the expected format: D.DDD or D.DDD.s+")
        (0, 0)
    }
    (maj, min)
  }

  private def majorVersion: Int = version._1
  private def minorVersion: Int = version._2

  /** start a new simulation
    *
    * @param state
    *   LoFirrtl circuit + annotations
    */
  override def createContext(state: CircuitState): SimulatorContext = {
    val simName = s"${VerilatorSimulator.name} ${VerilatorSimulator.majorVersion}.${VerilatorSimulator.minorVersion}"
    Caching.cacheSimulationBin(simName, state, createContextFromScratch, recreateCachedContext)
  }

  private def getSimulatorArgs(state: CircuitState): Array[String] = {
    state.annotations.view.collect { case PlusArgsAnnotation(args) => args }.flatten.toArray
  }

  private def recreateCachedContext(state: CircuitState): SimulatorContext = {
    // we will create the simulation in the target directory
    val targetDir = Compiler.requireTargetDir(state.annotations)
    val toplevel = TopmoduleInfo(state.circuit)

    val libFilename = if (JNAUtils.isWindows) { "V" + toplevel.name + ".exe" }
    else { "V" + toplevel.name }
    val libPath = targetDir / "verilated" / libFilename
    // the binary we created communicates using our standard IPC interface
    val coverageAnnos = loadCoverageAnnos(targetDir)
    val coverageFile = targetDir / "coverage.dat"
    def readCoverage(): List[(String, Long)] = {
      assert(os.exists(coverageFile), s"Could not find `$coverageFile` file!")
      VerilatorCoverage.loadCoverage(coverageAnnos, coverageFile, (majorVersion, minorVersion))
    }

    val args = getSimulatorArgs(state)
    val lib = JNAUtils.compileAndLoadJNAClass(libPath)
    new JNASimulatorContext(lib, targetDir, toplevel, VerilatorSimulator, args, Some(readCoverage))
  }

  private def createContextFromScratch(state: CircuitState): SimulatorContext = {
    // we will create the simulation in the target directory
    val targetDir = Compiler.requireTargetDir(state.annotations)
    val toplevel = TopmoduleInfo(state.circuit)

    // verbose output to stdout if we are in debug mode
    val verbose = state.annotations.contains(SimulatorDebugAnnotation)

    // Create the header files that verilator needs + a custom harness
    val waveformExt = Simulator.getWavformFormat(state.annotations)
    val cppHarness = generateHarness(targetDir, toplevel, waveformExt, verbose)

    // compile low firrtl to System Verilog for verilator to use
    val verilogState = Compiler.lowFirrtlToSystemVerilog(state, VerilatorCoverage.CoveragePasses)

    // turn SystemVerilog into C++ simulation
    val verilatedDir = runVerilator(toplevel.name, targetDir, cppHarness, state.annotations, verbose)

    // patch the coverage cpp provided with verilator only if Verilator is older than 4.202
    // Starting with Verilator 4.202, the whole patch coverage hack is no longer necessary
    require(
      majorVersion >= 4,
      s"Unsupported Verilator version: $majorVersion.$minorVersion. Only major version 4 and up is supported."
    )

    if (majorVersion == 4 && minorVersion < 202) {
      VerilatorPatchCoverageCpp(verilatedDir, majorVersion, minorVersion)
    }

    val libPath = compileSimulation(topName = toplevel.name, verilatedDir, verbose)

    // the binary we created communicates using our standard IPC interface
    val coverageAnnos = VerilatorCoverage.collectCoverageAnnotations(verilogState.annotations)
    if (Caching.shouldCache(state)) {
      saveCoverageAnnos(targetDir, coverageAnnos)
    }
    val coverageFile = targetDir / "coverage.dat"
    def readCoverage(): List[(String, Long)] = {
      assert(os.exists(coverageFile), s"Could not find `$coverageFile` file!")
      VerilatorCoverage.loadCoverage(coverageAnnos, coverageFile, (majorVersion, minorVersion))
    }

    val args = getSimulatorArgs(state)
    val lib = JNAUtils.compileAndLoadJNAClass(libPath)
    new JNASimulatorContext(lib, targetDir, toplevel, VerilatorSimulator, args, Some(readCoverage))
  }

  private def saveCoverageAnnos(targetDir: os.Path, annos: AnnotationSeq): Unit = {
    os.write.over(targetDir / "coverageAnnotations.json", JsonProtocol.serialize(annos))
  }

  private def loadCoverageAnnos(targetDir: os.Path): AnnotationSeq = {
    JsonProtocol.deserialize((targetDir / "coverageAnnotations.json").toIO)
  }

  private def compileSimulation(topName: String, verilatedDir: os.Path, verbose: Boolean): os.Path = {
    val target = s"V$topName"
    val processorCount = Runtime.getRuntime.availableProcessors.toString
    val cmd = Seq("make", "-C", verilatedDir.toString(), "-j", processorCount, "-f", s"V$topName.mk", target)
    val ret = run(cmd, null, verbose)
    assert(
      ret.exitCode == 0,
      s"Compilation of verilator generated code failed for circuit $topName in work dir $verilatedDir"
    )
    val simBinary = if (JNAUtils.isWindows) { verilatedDir / s"${target}.exe" }
    else { verilatedDir / target }
    assert(os.exists(simBinary), s"Failed to generate simulation binary: $simBinary")
    simBinary
  }

  /** executes verilator in order to generate a C++ simulation */
  private def runVerilator(
    topName:    String,
    targetDir:  os.Path,
    cppHarness: String,
    annos:      AnnotationSeq,
    verbose:    Boolean
  ): os.Path = {
    val verilatedDir = targetDir / "verilated"

    removeOldCode(verilatedDir, verbose)
    val flagAnnos = VerilatorLinkFlags(JNAUtils.ldFlags) +: VerilatorCFlags(JNAUtils.ccFlags) +: annos
    val flags = generateFlags(topName, verilatedDir, flagAnnos)
    val cmd = List(
      if (JNAUtils.isWindows) { "verilator_bin" }
      else { "verilator" },
      "--cc",
      "--exe",
      cppHarness
    ) ++ flags ++ List(s"$topName.sv")
    val ret = run(cmd, targetDir, verbose)

    assert(ret.exitCode == 0, s"verilator command failed on circuit ${topName} in work dir $targetDir")
    verilatedDir
  }

  private def removeOldCode(verilatedDir: os.Path, verbose: Boolean): Unit = {
    if (os.exists(verilatedDir)) {
      if (verbose) println(s"Deleting stale Verilator object directory: $verilatedDir")
      os.remove.all(verilatedDir)
    }
  }

  private def run(cmd: Seq[String], cwd: os.Path, verbose: Boolean): os.CommandResult = {
    if (verbose) {
      // print the command and pipe the output to stdout
      println(quoteCmdArgs(cmd))
      os.proc(cmd)
        .call(cwd = cwd, stdout = os.ProcessOutput.Readlines(println), stderr = os.ProcessOutput.Readlines(println))
    } else {
      os.proc(cmd).call(cwd = cwd)
    }
  }

  private def DefaultCFlags(topName: String) = List(
    "-Os",
    "-DVL_USER_STOP",
    "-DVL_USER_FATAL",
    "-DVL_USER_FINISH", // this is required because we ant to overwrite the vl_finish function!
    s"-DTOP_TYPE=V$topName"
  )

  private def DefaultFlags(topName: String, verilatedDir: os.Path, cFlags: Seq[String], ldFlags: Seq[String]) = List(
    "--assert", // we always enable assertions
    "--coverage-user", // we always enable use coverage
    "-Wno-fatal",
    "-Wno-WIDTH",
    "-Wno-STMTDLY",
    "--top-module",
    topName,
    "+define+TOP_TYPE=V" + topName,
    // flags passed to the C++ compiler
    "-CFLAGS",
    cFlags.mkString(" "),
    // name of the directory that verilator generates the C++ model + Makefile in
    "-Mdir",
    verilatedDir.toString()
  ) ++ (if (ldFlags.nonEmpty) Seq("-LDFLAGS", ldFlags.mkString(" ")) else Seq())

  // documentation of Verilator flags: https://verilator.org/guide/latest/exe_verilator.html#
  private def generateFlags(topName: String, verilatedDir: os.Path, annos: AnnotationSeq): Seq[String] = {
    val waveformExt = Simulator.getWavformFormat(annos)
    val targetDir = Compiler.requireTargetDir(annos)

    // generate C flags
    val userCFlags = annos.collect { case VerilatorCFlags(f) => f }.flatten
    // some older versions of Verilator seem to not set VM_TRACE_FST correctly, thus:
    val fstCFlag = if (waveformExt == "fst") Seq("-DVM_TRACE_FST=1") else Seq()
    val cFlags = DefaultCFlags(topName) ++ fstCFlag ++ userCFlags
    val ldFlags = annos.collect { case VerilatorLinkFlags(f) => f }.flatten

    // combine all flags
    val userFlags = annos.collectFirst { case VerilatorFlags(f) => f }.getOrElse(Seq.empty)
    val waveformFlags = waveformExt match {
      case "vcd" => List("--trace")
      case "fst" => List("--trace-fst")
      case ""    => List()
      case other => throw new RuntimeException(s"Unsupported waveform format: $other")
    }
    val flags =
      DefaultFlags(topName, verilatedDir, cFlags, ldFlags) ++ waveformFlags ++ BlackBox.fFileFlags(
        targetDir
      ) ++ userFlags
    flags
  }

  private def generateHarness(
    targetDir:   os.Path,
    toplevel:    TopmoduleInfo,
    waveformExt: String,
    verbose:     Boolean
  ): String = {
    val topName = toplevel.name

    // create a custom c++ harness
    val cppHarnessFileName = s"${topName}-harness.cpp"
    val vcdFile = targetDir / (s"$topName." + waveformExt)
    val code = VerilatorCppJNAHarnessGenerator.codeGen(
      toplevel,
      vcdFile,
      targetDir,
      majorVersion = majorVersion,
      minorVersion = minorVersion,
      verbose = verbose
    )
    os.write.over(targetDir / cppHarnessFileName, code)

    cppHarnessFileName
  }
}

/** Changes the file generated by verilator to generate per instance and not per module coverage. This is required in
  * order to satisfy our generic TestCoverage interface for which the simulator needs to return per instance coverage
  * counts. See: https://github.com/verilator/verilator/issues/2793
  */
private object VerilatorPatchCoverageCpp {
  private val CallNeedle = "VL_COVER_INSERT("
  private val CallReplacement = "CHISEL_VL_COVER_INSERT("
  private val CoverageStartNeedle = "// Coverage"

  def apply(dir: os.Path, major: Int, minor: Int): Unit = {
    assert(major == 4 && minor < 202, "Starting with Verilator 4.202 this hack is no longer necessary!")
    val files = loadFiles(dir)
    files.foreach { case (cppFile, lines) =>
      replaceCoverage(cppFile, lines, minor)
      doWrite(cppFile, lines)
    }
  }

  private def replaceCoverage(cppFile: os.Path, lines: Array[String], minor: Int): Unit = {
    // we add our code at the beginning of the coverage section
    val coverageStart = findLine(CoverageStartNeedle, cppFile, lines)
    lines(coverageStart) += "\n" + CustomCoverInsertCode(withCtxPtr = minor >= 200) + "\n"

    // then we replace the call
    val call = findLine(CallNeedle, cppFile, lines)
    val callLine = lines(call).replace(CallNeedle, CallReplacement)
    lines(call) = callLine
  }

  private def loadFiles(dir: os.Path): Seq[(os.Path, Array[String])] = {
    if (!os.exists(dir) || !os.isDir(dir)) {
      error(s"Failed to find directory $dir")
    }

    // find all cpp files generated by verilator
    val cppFiles = os.list(dir).filter(os.isFile).filter { f =>
      val name = f.last
      name.startsWith("V") && name.endsWith(".cpp")
    }

    // filter out files that do not contain any coverage definitions
    cppFiles.map(f => (f, FileUtils.getLines(f).toArray)).filter { case (name, lines) =>
      findLineOption(CoverageStartNeedle, lines).isDefined
    }
  }

  private def findLineOption(needle: String, lines: Iterable[String]): Option[Int] =
    lines.map(_.trim).zipWithIndex.find(_._1.startsWith(needle)).map(_._2)

  private def findLine(needle: String, filename: os.Path, lines: Iterable[String]): Int =
    findLineOption(needle, lines).getOrElse(error(s"Failed to find line `$needle` in $filename."))

  private def doWrite(file: os.Path, lines: Array[String]): Unit = os.write.over(file, lines.mkString("\n"))

  private def error(msg: String): Nothing = {
    throw new RuntimeException(msg + "\n" + "Please file an issue and include the output of `verilator --version`")
  }

  private def CustomCoverInsertCode(withCtxPtr: Boolean): String = {
    val argPrefix = if (withCtxPtr) "VerilatedCovContext* covcontextp, " else ""
    val callArgPrefix = if (withCtxPtr) "covcontextp, " else ""
    val cov = if (withCtxPtr) "covcontextp->" else "VerilatedCov::"

    s"""#ifndef CHISEL_VL_COVER_INSERT
       |#define CHISEL_VL_COVER_INSERT(${callArgPrefix}countp, ...) \\
       |    VL_IF_COVER(${cov}_inserti(countp); ${cov}_insertf(__FILE__, __LINE__); \\
       |                chisel_insertp(${callArgPrefix}"hier", name(), __VA_ARGS__))
       |
       |#ifdef VM_COVERAGE
       |static void chisel_insertp(${argPrefix}
       |  const char* key0, const char* valp0, const char* key1, const char* valp1,
       |  const char* key2, int lineno, const char* key3, int column,
       |  const char* key4, const std::string& hier_str,
       |  const char* key5, const char* valp5, const char* key6, const char* valp6,
       |  const char* key7 = nullptr, const char* valp7 = nullptr) {
       |
       |    std::string val2str = vlCovCvtToStr(lineno);
       |    std::string val3str = vlCovCvtToStr(column);
       |    ${cov}_insertp(
       |        key0, valp0, key1, valp1, key2, val2str.c_str(),
       |        key3, val3str.c_str(), key4, hier_str.c_str(),
       |        key5, valp5, key6, valp6, key7, valp7,
       |        // turn on per instance cover points
       |        "per_instance", "1");
       |}
       |#endif // VM_COVERAGE
       |#endif // CHISEL_VL_COVER_INSERT
       |""".stripMargin
  }
}


// SPDX-License-Identifier: Apache-2.0

package chiseltest.simulator.jna

import chiseltest.simulator._
import firrtl2.logger.LazyLogging

/** This context works with a simulation binary that communicates through the Java Native Access library.
  * @param so
  *   interface to the dynamic simulation library
  * @param targetDir
  *   simulation target directory
  * @param toplevel
  *   information about the interface exposed by the module at the top of the RTL hierarchy
  * @param sim
  *   simulator that generated the binary
  * @param args
  *   command line arguments to the simulator (eg. Verilog plusargs)
  * @param readCoverageFile
  *   function that parses the coverage file and returns the list of counts
  */
private[chiseltest] class JNASimulatorContext(
  so:               TesterSharedLibInterface,
  targetDir:        os.Path,
  toplevel:         TopmoduleInfo,
  override val sim: Simulator,
  args:             Array[String],
  readCoverageFile: Option[() => List[(String, Long)]] = None)
    extends SimulatorContext
    with LazyLogging {
  toplevel.requireNoMultiClock()
  private val allSignals = toplevel.inputs ++ toplevel.outputs
  private val isWide = allSignals.filter(_.width > 64).map(_.name).toSet
  private val mask64 = (BigInt(1) << 64) - 1
  private val signalWidth = allSignals.map(s => s.name -> s.width).toMap

  private var isStale = true
  private val signalToId = (toplevel.inputs ++ toplevel.outputs).map(_.name).zipWithIndex.toMap
  private val idToMask = (toplevel.inputs ++ toplevel.outputs).map(_.width).map(w => (BigInt(1) << w) - 1).toIndexedSeq
  private val idIsSigned = (toplevel.inputs ++ toplevel.outputs).map(_.signed).toIndexedSeq

  // Pass command line arguments to the simulator
  so.setArgs(args)

  private def update(): Unit = {
    assert(isRunning)
    so.update()
    isStale = false
  }

  private def takeSteps(cycles: Int): Long = {
    assert(isRunning)
    require(cycles > 0)
    so.step(cycles)
  }

  private def getId(signal: String): Int =
    signalToId.getOrElse(signal, throw new RuntimeException(s"Unknown signal: $signal"))

  override def poke(signal: String, value: BigInt): Unit = {
    assert(isRunning)
    val signalId = getId(signal)
    val mask = idToMask(signalId)
    val maskedValue = value & mask
    if (isWide(signal)) {
      val width = signalWidth(signal)
      val words = (width + 63) / 64
      var remaining = maskedValue
      (0 until words).foreach { ii =>
        val part = (remaining & mask64).toLong
        so.pokeWide(signalId, ii, part)
        remaining = remaining >> 64
      }
    } else {
      so.poke(signalId, maskedValue.toLong)
    }
    isStale = true
  }

  override def peek(signal: String): BigInt = {
    assert(isRunning)

    val signalId = getId(signal)
    val width = signalWidth(signal)
    val unsigned = if (isWide(signal)) {
      val words = (width + 63) / 64
      var value = BigInt(0)
      (0 until words).foreach { ii =>
        val word = BigInt(so.peekWide(signalId, ii)) & mask64
        value = value | (word << (ii * 64))
      }
      value
    } else {
      so.peek(signalId) & mask64
    }
    if (idIsSigned(signalId)) { toSigned(unsigned, width) }
    else { unsigned }
  }

  private def toSigned(v: BigInt, width: Int): BigInt = {
    val isNegative = ((v >> (width - 1)) & 1) == 1
    if (isNegative) {
      val mask = (BigInt(1) << width) - 1
      val twosComp = ((~v) + 1) & mask
      -twosComp
    } else { v }
  }

  private def defaultClock = toplevel.clocks.headOption
  override def step(n: Int): StepResult = {
    assert(isRunning)
    defaultClock match {
      case Some(_) =>
      case None    => throw NoClockException(toplevel.name)
    }
    update()
    val r = takeSteps(n)
    val status = (r >> 32) & 3
    if (status == 0) {
      StepOk
    } else if (status == 3) {
      val msg = "The simulator has encountered an unrecoverable error.\n" +
        "Please consult the standard output and error for more details."
      throw new RuntimeException(msg)
    } else {
      val isFailure = status != 1
      val after = r & 0xffffffffL
      StepInterrupted(after.toInt, isFailure, List())
    }
  }

  private var isRunning = true
  override def finish(): Unit = {
    assert(isRunning, "Simulator is already stopped! Are you trying to call finish twice?")
    so.finish()
    isRunning = false
  }

  private val coverageFile = targetDir / "coverage.dat"
  override def getCoverage(): List[(String, Long)] = {
    if (isRunning) {
      so.writeCoverage(coverageFile.toString())
    }
    assert(os.exists(coverageFile), s"Could not find `$coverageFile` file!")
    readCoverageFile.get()
  }

  override def resetCoverage(): Unit = {
    assert(isRunning)
    so.resetCoverage()
  }
}


// SPDX-License-Identifier: Apache-2.0

package treadle2.executable

/** ClockInfo associates a clock with the given name and period and offset The period is in an arbitrary number of
  * ticks. The VCD logger currently sets these ticks to be nanosecond(ns). The first up transition takes place after
  * initialOffset ticks. One or more clocks can be specified through the TreadleOptions clockInfo as a Seq of
  * ClockInfo's or from string command line based --fint-clock-info or -fici which use the format
  * clock-name[:period[:initial-offset] ]
  *
  * @param name
  *   the signal name of a clock
  * @param period
  *   how many ticks between rising edges of this clock
  * @param initialOffset
  *   the tick where the first up transition takes place.
  */
case class ClockInfo(
  name:          String = ClockInfo.DefaultName,
  period:        Long = ClockInfo.DefaultPeriod,
  initialOffset: Long = ClockInfo.DefaultOffset) {
  if (period % 2 != 0) {
    throw TreadleException(s"Error: Clock period must be divisible by 2: Found $this")
  }

  val upPeriod:   Long = period / 2
  val downPeriod: Long = period - upPeriod
  if (initialOffset < 0) {
    throw TreadleException(s"initialOffset in ClockInfo for $name must be positive. Found value $initialOffset")
  }

  def prettyString: String = {

    f"$name%-40.40s period $period%5d,  $detail%15s, first up at $initialOffset"
  }
}

/** The default settings for a single clock are here. Units are in arbitrary ticks
  */
object ClockInfo {
  val DefaultName:   String = "clock"
  val DefaultPeriod: Long = 10L
  val DefaultOffset: Long = 1L
}

def detail = s"(up: $upPeriod, down: $downPeriod)"


// SPDX-License-Identifier: Apache-2.0

package chiseltest.backends.icarus

import chisel3._
import chisel3.util._
import chiseltest._
import chiseltest.simulator.{PlusArgsAnnotation, RequiresIcarus}
import org.scalatest.flatspec.AnyFlatSpec

import scala.util.Random

class BlackBoxAdderIO extends Bundle {
  val a = Input(UInt(8.W))
  val b = Input(UInt(8.W))
  val q = Output(UInt(8.W))
}

class BlackBoxAdder extends BlackBox with HasBlackBoxInline {
  val io = IO(new BlackBoxAdderIO).suggestName("io")
  setInline(
    "BlackBoxAdder.v",
    """module BlackBoxAdder(a, b, q);
      |input [7:0] a, b;
      |output [7:0] q;
      |assign q = a + b;
      |endmodule
      |""".stripMargin
  )
}

class BlackBoxAdderWrapper extends Module {
  val io = IO(new BlackBoxAdderIO)
  val m = Module(new BlackBoxAdder)
  m.io <> io
}

// Inspired by plusarg_reader in rocket-chip
class PlusArgReader extends BlackBox with HasBlackBoxInline {
  val io = IO(new Bundle {
    val out = Output(UInt(32.W))
  })
  setInline(
    "PlusArgReader.v",
    """module PlusArgReader(
      |  output [31:0] out
      |);
      |  reg [32:0] argument;
      |  assign out = argument;
      |  initial begin
      |    if (!$value$plusargs("ARGUMENT=%d", argument)) begin
      |      argument = 32'hdeadbeef;
      |    end
      |  end
      |endmodule
      |""".stripMargin
  )
}

  val annos = Seq(IcarusBackendAnnotation)

  it should "support IcarusVerilog black boxes" taggedAs RequiresIcarus in {
    val rand = new Random()
    val mask = (BigInt(1) << 8) - 1
    test(new BlackBoxAdderWrapper).withAnnotations(annos) { dut =>
      (0 until 1000).foreach { ii =>
        val a = BigInt(8, rand)
        val b = BigInt(8, rand)
        val q = (a + b) & mask
        dut.io.a.poke(a.U)
        dut.io.b.poke(b.U)
        dut.io.q.expect(q.U)
      }
    }
  }

  it should "support reading IcarusVerilog plusargs" taggedAs RequiresIcarus in {
    for (plusarg <- List(0, 123, 456)) {
      val allAnnos = annos :+ PlusArgsAnnotation(s"+ARGUMENT=$plusarg" :: Nil)
      test(new PlusArgReaderWrapper(plusarg)).withAnnotations(allAnnos) { dut =>
        dut.reset.poke(true.B)
        dut.clock.step()
        dut.reset.poke(false.B)
        dut.clock.step()
      }
    }
  }
}

class PlusArgReaderWrapper(expected: Int) extends Module {
  val reader = Module(new PlusArgReader)
  val msg = s"Expected $expected, got %x.\n" // this works around the fact that s".." is forbidden in the assert
  assert(reader.io.out === expected.U, msg, reader.io.out)
}


package chiseltest.formal

import chisel3._
import chisel3.util._
import chiseltest._
import firrtl2.AnnotationSeq
import org.scalatest.flatspec.AnyFlatSpec

class MagicPacketTrackerTests extends AnyFlatSpec with ChiselScalatestTester with Formal with FormalBackendOption {
  behavior.of("MagicPacketTracker")

  private def defaultOptions: AnnotationSeq = Seq(BoundedCheck(7), DefaultBackend)
  private val DefaultDepth = 3
  private val DefaultDepthPow2 = 4 // some queues only work with power of two

  it should "notice if the counter overflows" taggedAs FormalTag in {
    assume(DefaultBackend != CVC4EngineAnnotation, "CVC4 is too slow with some of these tests (5h+)")
    // counter overflows can happen if the user specified a depth that is too small
    val e = intercept[FailedBoundedCheckException] {
      verify(new PacketTrackerCounterOverflowTest, Seq(BoundedCheck(16), DefaultBackend))
    }
    assert(e.failAt == 3) // without the overflow check, this fails at 4
  }

  it should "verify the BubbleFifo" taggedAs FormalTag in {
    assume(DefaultBackend != CVC4EngineAnnotation, "CVC4 is too slow with some of these tests (5h+)")
    verify(new FifoTestWrapper(new BubbleFifo(UInt(16.W), 4)), defaultOptions)
  }

  it should "verify QueueV0" taggedAs FormalTag in {
    assume(DefaultBackend != CVC4EngineAnnotation, "CVC4 is too slow with some of these tests (5h+)")
    verify(new QueueFormalTest(new MyQueueV0(32)), defaultOptions)
  }

  it should "verify QueueV1" taggedAs FormalTag in {
    assume(DefaultBackend != CVC4EngineAnnotation, "CVC4 is too slow with some of these tests (5h+)")
    verify(new QueueFormalTest(new MyQueueV1(DefaultDepth, 32)), defaultOptions)
  }

  it should "find bug in QueueV2" taggedAs FormalTag in {
    assume(DefaultBackend != CVC4EngineAnnotation, "CVC4 is too slow with some of these tests (5h+)")
    val e = intercept[FailedBoundedCheckException] {
      verify(new QueueFormalTest(new MyQueueV2(DefaultDepth, 32)), defaultOptions)
    }
    assert(e.failAt == 3)
  }

  it should "verify QueueV3" taggedAs FormalTag in {
    assume(DefaultBackend != CVC4EngineAnnotation, "CVC4 is too slow with some of these tests (5h+)")
    verify(new QueueFormalTest(new MyQueueV3(DefaultDepthPow2, 32)), defaultOptions)
  }

  it should "verify QueueV4" taggedAs FormalTag in {
    assume(DefaultBackend != CVC4EngineAnnotation, "CVC4 is too slow with some of these tests (5h+)")
    verify(new QueueFormalTest(new MyQueueV4(DefaultDepthPow2, 32)), defaultOptions)
  }

  it should "verify QueueV5" taggedAs FormalTag in {
    assume(DefaultBackend != CVC4EngineAnnotation, "CVC4 is too slow with some of these tests (5h+)")
    verify(new QueueFormalTest(new MyQueueV5(DefaultDepthPow2, 32)), defaultOptions)
  }

  it should "find bug in QueueV6 w/ pipe = false" taggedAs FormalTag in {
    assume(DefaultBackend != CVC4EngineAnnotation, "CVC4 is too slow with some of these tests (5h+)")
    val e = intercept[FailedBoundedCheckException] {
      verify(new QueueFormalTest(new MyQueueV6(DefaultDepth, 32, pipe = false, fixed = false)), defaultOptions)
    }
    assert(e.failAt == 3)
  }

  it should "verify fix for QueueV6 w/ pipe = false" taggedAs FormalTag in {
    assume(DefaultBackend != CVC4EngineAnnotation, "CVC4 is too slow with some of these tests (5h+)")
    verify(new QueueFormalTest(new MyQueueV6(DefaultDepth, 32, pipe = false, fixed = true)), defaultOptions)
  }

  it should "find bug in QueueV6 w/ pipe = true" taggedAs FormalTag in {
    assume(DefaultBackend != CVC4EngineAnnotation, "CVC4 is too slow with some of these tests (5h+)")
    val e = intercept[FailedBoundedCheckException] {
      verify(new QueueFormalTest(new MyQueueV6(DefaultDepth, 32, pipe = true, fixed = false)), defaultOptions)
    }
    assert(e.failAt == 2)
  }

  it should "verify fix for QueueV6 w/ pipe = true" taggedAs FormalTag in {
    assume(DefaultBackend != CVC4EngineAnnotation, "CVC4 is too slow with some of these tests (5h+)")
    verify(new QueueFormalTest(new MyQueueV6(DefaultDepth, 32, pipe = true, fixed = true)), defaultOptions)
  }

  it should "verify some versions of the Queue from the Chisel standard library" taggedAs FormalTag in {
    assume(DefaultBackend != CVC4EngineAnnotation, "CVC4 is too slow with some of these tests (5h+)")
    verify(new ChiselQueueTest(DefaultDepth, 32, false, false), defaultOptions)
    verify(new ChiselQueueTest(DefaultDepth, 32, false, true), defaultOptions)
    verify(new ChiselQueueTest(DefaultDepth, 32, true, false), defaultOptions)
    verify(new ChiselQueueTest(DefaultDepth, 32, true, true), defaultOptions)
  }
}

private class PacketTrackerCounterOverflowTest extends Module {
  // our queue can take up to 8 elements
  val dut = Module(new Queue(UInt(32.W), 8))
  val io = IO(chiselTypeOf(dut.io)); io <> dut.io
  // however for some reason we misconfigure the depth of the tracker
  MagicPacketTracker(enq = dut.io.enq, deq = dut.io.deq, depth = 3)
}

// very similar to the QueueSpec in the chisel3 tests, but a formal test
private class ChiselQueueTest(queueDepth: Int, bitWidth: Int, useSyncReadMem: Boolean, hasFlush: Boolean)
    extends Module {
  val dut = Module(new Queue(UInt(bitWidth.W), queueDepth, useSyncReadMem = useSyncReadMem, hasFlush = hasFlush))
  val io = IO(chiselTypeOf(dut.io)); io <> dut.io
  // we need to tie the flush pin to false, because the MagicPacketTracker cannot (currently)
  // handle flushes, it will complain about a missing packet if a flush happens
  dut.io.flush.foreach(_ := false.B)
  MagicPacketTracker(enq = dut.io.enq, deq = dut.io.deq, depth = queueDepth)
}

// FIFOs from https://github.com/freechipsproject/ip-contributions

private class FifoTestWrapper(fifo: => Fifo[UInt]) extends Module {
  val dut = Module(fifo)
  val io = IO(chiselTypeOf(dut.io)); io <> dut.io
  MagicPacketTracker(enq = dut.io.enq, deq = dut.io.deq, depth = dut.depth)
}

/** FIFO IO with enqueue and dequeue ports using the ready/valid interface.
  */
private class FifoIO[T <: Data](private val gen: T) extends Bundle {
  val enq = Flipped(new DecoupledIO(gen))
  val deq = new DecoupledIO(gen)
}

/** Base class for all FIFOs.
  */
private abstract class Fifo[T <: Data](gen: T, val depth: Int) extends Module {
  val io = IO(new FifoIO(gen))

  assert(depth > 0, "Number of buffer elements needs to be larger than 0")
}

/** A simple bubble FIFO. Maximum throughput is one word every two clock cycles.
  */
private class BubbleFifo[T <: Data](gen: T, depth: Int) extends Fifo(gen: T, depth: Int) {

  private class Buffer() extends Module {
    val io = IO(new FifoIO(gen))

    val fullReg = RegInit(false.B)
    val dataReg = Reg(gen)

    when(fullReg) {
      when(io.deq.ready) {
        fullReg := false.B
      }
    }.otherwise {
      when(io.enq.valid) {
        fullReg := true.B
        dataReg := io.enq.bits
      }
    }

    io.enq.ready := !fullReg
    io.deq.valid := fullReg
    io.deq.bits := dataReg
  }

  private val buffers = Array.fill(depth) { Module(new Buffer()) }
  for (i <- 0 until depth - 1) {
    buffers(i + 1).io.enq <> buffers(i).io.deq
  }

  io.enq <> buffers(0).io.enq
  io.deq <> buffers(depth - 1).io.deq
}

///////////////////////////////////////////////////////
// Queues from Scott Beamer's agile hardware lectures
///////////////////////////////////////////////////////

private class QueueFormalTest(makeQueue: => IsQueue) extends Module {
  val dut = Module(makeQueue)
  val io = IO(chiselTypeOf(dut.io)); io <> dut.io
  MagicPacketTracker(enq = dut.io.enq, deq = dut.io.deq, depth = dut.numEntries, debugPrint = false)
}

private trait IsQueue extends Module {
  def io:         QueueIO
  def numEntries: Int
}

private class QueueIO(bitWidth: Int) extends Bundle {
  val enq = Flipped(Decoupled(UInt(bitWidth.W)))
  val deq = Decoupled(UInt(bitWidth.W))
}

private class MyQueueV0(bitWidth: Int) extends Module with IsQueue {
  override val numEntries = 1
  val io = IO(new QueueIO(bitWidth))
  val entry = Reg(UInt(bitWidth.W))
  val full = RegInit(false.B)
  io.enq.ready := !full || io.deq.fire
  io.deq.valid := full
  io.deq.bits := entry
  when(io.deq.fire) {
    full := false.B
  }
  when(io.enq.fire) {
    entry := io.enq.bits
    full := true.B
  }
}

private class MyQueueV1(val numEntries: Int, bitWidth: Int) extends Module with IsQueue {
  val io = IO(new QueueIO(bitWidth))
  require(numEntries > 0)
  // enqueue into index numEntries-1 (last) and dequeue from index 0 (head)
  val entries = Seq.fill(numEntries)(Reg(UInt(bitWidth.W)))
  val fullBits = Seq.fill(numEntries)(RegInit(false.B))
  val shiftDown = io.deq.fire || !fullBits.head
  io.enq.ready := !fullBits.last || shiftDown
  io.deq.valid := fullBits.head
  io.deq.bits := entries.head
  when(shiftDown) { // dequeue / shift
    for (i <- 0 until numEntries - 1) {
      entries(i) := entries(i + 1)
      fullBits(i) := fullBits(i + 1)
    }
    fullBits.last := false.B
  }
  when(io.enq.fire) { // enqueue
    entries.last := io.enq.bits
    fullBits.last := true.B
  }
  //     when (shiftDown || io.enq.fire) {
  //         entries.foldRight(io.enq.bits){(thisEntry, lastEntry) => thisEntry := lastEntry; thisEntry}
  //         fullBits.foldRight(io.enq.fire){(thisEntry, lastEntry) => thisEntry := lastEntry; thisEntry}
  //
}

private class MyQueueV2(val numEntries: Int, bitWidth: Int) extends Module with IsQueue {
  val io = IO(new QueueIO(bitWidth))
  require(numEntries > 0)
  // enqueue into lowest empty and dequeue from index 0 (head)
  val entries = Reg(Vec(numEntries, UInt(bitWidth.W)))
  val fullBits = RegInit(VecInit(Seq.fill(numEntries)(false.B)))
  val emptyBits = fullBits.map { !_ }

  io.deq.valid := fullBits.head
  io.deq.bits := entries.head
  when(io.deq.fire) { // dequeue & shift up
    for (i <- 0 until numEntries - 1) {
      entries(i) := entries(i + 1)
      fullBits(i) := fullBits(i + 1)
    }
    fullBits.last := false.B
  }
  when(io.enq.fire) { // priority enqueue
    val writeIndex = PriorityEncoder(emptyBits)
    entries(writeIndex) := io.enq.bits
    fullBits(writeIndex) := true.B
  }
}

private class MyQueueV3(val numEntries: Int, bitWidth: Int) extends Module with IsQueue {
  val io = IO(new QueueIO(bitWidth))
  require(numEntries > 1)
  require(isPow2(numEntries))
  val entries = Reg(Vec(numEntries, UInt(bitWidth.W))) // Mem?
  val enqIndex = RegInit(0.U(log2Ceil(numEntries).W))
  val deqIndex = RegInit(0.U(log2Ceil(numEntries).W))
  val empty = enqIndex === deqIndex
  val full = (enqIndex +% 1.U) === deqIndex
  io.enq.ready := !full
  io.deq.valid := !empty
  io.deq.bits := entries(deqIndex)
  when(io.deq.fire) {
    deqIndex := deqIndex +% 1.U
  }
  when(io.enq.fire) {
    entries(enqIndex) := io.enq.bits
    enqIndex := enqIndex +% 1.U
  }
}

private class MyQueueV4(val numEntries: Int, bitWidth: Int) extends Module with IsQueue {
  val io = IO(new QueueIO(bitWidth))
  require(numEntries > 1)
  require(isPow2(numEntries))
  val entries = Reg(Vec(numEntries, UInt(bitWidth.W)))
  val enqIndex = RegInit(0.U(log2Ceil(numEntries).W))
  val deqIndex = RegInit(0.U(log2Ceil(numEntries).W))
  val maybeFull = RegInit(false.B)
  val empty = enqIndex === deqIndex && !maybeFull
  val full = enqIndex === deqIndex && maybeFull
  io.enq.ready := !full
  io.deq.valid := !empty
  io.deq.bits := entries(deqIndex)
  when(io.deq.fire) {
    deqIndex := deqIndex +% 1.U
    when(enqIndex =/= deqIndex) {
      maybeFull := false.B
    }
  }
  when(io.enq.fire) {
    entries(enqIndex) := io.enq.bits
    enqIndex := enqIndex +% 1.U
    when((enqIndex +% 1.U) === deqIndex) {
      maybeFull := true.B
    }
  }
}

private class MyQueueV5(val numEntries: Int, bitWidth: Int) extends Module with IsQueue {
  val io = IO(new QueueIO(bitWidth))
  require(numEntries > 1)
  require(isPow2(numEntries))
  val entries = Reg(Vec(numEntries, UInt(bitWidth.W)))
  val enqIndex = RegInit(0.U(log2Ceil(numEntries).W))
  val deqIndex = RegInit(0.U(log2Ceil(numEntries).W))
  val maybeFull = RegInit(false.B)
  val empty = enqIndex === deqIndex && !maybeFull
  val full = enqIndex === deqIndex && maybeFull
  io.enq.ready := !full || io.deq.ready // NOTE: io.enq.ready now attached to io.deq.ready
  io.deq.valid := !empty
  io.deq.bits := entries(deqIndex)
  when(io.deq.fire) {
    deqIndex := deqIndex +% 1.U
    when(enqIndex =/= deqIndex) {
      maybeFull := false.B
    }
  }
  when(io.enq.fire) {
    entries(enqIndex) := io.enq.bits
    enqIndex := enqIndex +% 1.U
    when((enqIndex +% 1.U) === deqIndex) {
      maybeFull := true.B
    }
  }
}

private class MyQueueV6(val numEntries: Int, bitWidth: Int, pipe: Boolean, fixed: Boolean) extends Module with IsQueue {
  val io = IO(new QueueIO(bitWidth))
  require(numEntries > 1)
  //     require(isPow2(numEntries))    // no longer needed
  val entries = Mem(numEntries, UInt(bitWidth.W))
  val enqIndex = Counter(numEntries)
  val deqIndex = Counter(numEntries)
  val maybeFull = RegInit(false.B)
  val indicesEqual = enqIndex.value === deqIndex.value
  val empty = indicesEqual && !maybeFull
  val full = indicesEqual && maybeFull
  if (pipe)
    io.enq.ready := !full || io.deq.ready
  else
    io.enq.ready := !full
  io.deq.valid := !empty
  io.deq.bits := entries(deqIndex.value)
  if (fixed) {
    when(io.deq.fire =/= io.enq.fire) {
      maybeFull := io.enq.fire
    }
  } else {
    when(indicesEqual && io.deq.fire =/= io.enq.fire) {
      maybeFull := !maybeFull
    }
  }
  when(io.deq.fire) {
    deqIndex.inc()
  }
  when(io.enq.fire) {
    entries(enqIndex.value) := io.enq.bits
    enqIndex.inc()
  }
}

io.enq.ready := emptyBits.reduce { _ || _ } // any empties?


// SPDX-License-Identifier: Apache-2.0

package chiseltest.formal.examples

import chisel3._
import chisel3.util._
import chisel3.experimental.{annotate, ChiselAnnotation}
import chiseltest._
import chiseltest.formal._
import org.scalatest.flatspec.AnyFlatSpec
import chiseltest.formal.FormalTag

/** These small examples demonstrate how the chisel Past operator can be used to emulate simple SVA constructs. All
  * demos are based on the awesome SVA tutorial from SymbioticEDA: https://github.com/SymbioticEDA/sva-demos
  */
class SvaDemos extends AnyFlatSpec with ChiselScalatestTester with Formal with FormalBackendOption {
  "Demo1" should "fail a bounded check 17 cycles after reset" taggedAs FormalTag in {
    val e = intercept[FailedBoundedCheckException] {
      verify(new SvaDemo1, Seq(BoundedCheck(20), DefaultBackend))
    }
    assert(e.failAt == 17)
  }

  Seq("A" -> (() => new SvaDemo2), "B" -> (() => new SvaDemo2B), "C" -> (() => new SvaDemo2C)).foreach {
    case (n, gen) =>
      s"Demo2$n" should "fail a bounded check 15 cycles after reset" taggedAs FormalTag in {
        val e = intercept[FailedBoundedCheckException] {
          verify(gen(), Seq(BoundedCheck(20), DefaultBackend))
        }
        assert(e.failAt == 15)
      }
  }

  "Demo3" should "fail a bounded check 16 cycles after reset" taggedAs FormalTag in {
    val e = intercept[FailedBoundedCheckException] {
      verify(new SvaDemo3, Seq(BoundedCheck(20), DefaultBackend))
    }
    assert(e.failAt == 16)
  }
}

class SvaDemo1 extends SvaDemoModule {
  seqs(
    //       01234567890123456789
    reset = "-_____-____-________",
    a = "_--___-___-______-__",
    b = "__--__-__________-__"
  )

  // we can replace the a |=> b with our past operator
  when(past(a)) { assert(b) }
}

class SvaDemo2 extends SvaDemoModule {
  seqs(
    //       0123456789012345678901
    reset = "-_____-_______________",
    a = "_-____-_______-_______",
    b = "___-__-__________-____"
  )

  // we need to manually express the two different possibilities
  assert(IfThen(past(a, 2), b) || past(IfThen(past(a), b)))
}

/** This version changes the trace to test the (allowed) possibility of b going high the cycle after a */
class SvaDemo2B extends SvaDemoModule {
  seqs(
    //       0123456789012345678901
    reset = "-_____-_______________",
    a = "_-____-___-___-_______",
    b = "___-__-____-_____-____"
  )

  // we need to manually express the two different possibilities
  assert(IfThen(past(a, 2), b) || past(IfThen(past(a), b)))
}

/** This version shows that b can be high independent from a */
class SvaDemo2C extends SvaDemoModule {
  seqs(
    //       0123456789012345678901
    reset = "-_____-_______________",
    a = "_-____-_______-_______",
    b = "---------------__-----"
  )

  // we need to manually express the two different possibilities
  // assert property (a |-> ##[1:2] b);
  assert(IfThen(past(a, 2), b) || past(IfThen(past(a), b)))
}

class SvaDemo3 extends SvaDemoModule {
  seqs(
    //       0123456789012345678901
    reset = "-_______-___________",
    a = "_--___-_______-_____",
    b = "__--___-________--__",
    c = "____-_____________-_"
  )

  // we need to manually express the property
  // note that our translation will fail 2 cycles later than the original
  // assert property ($rose(a) |=> b[*2] ##1 c);
  when(past(rose(a), 3)) {
    assert(past(b, 2) && past(b) && c)
  }
}

class SvaDemoModule extends Module {
  private val preset = IO(Input(AsyncReset()))
  annotateAsPreset(preset.toTarget)
  private def seq(values: String): Bool = {
    withReset(preset) {
      require(values.forall(c => c == '_' || c == '-'), s"$values contains invalid characters")
      val (counter, wrap) = Counter(true.B, values.length + 1)
      val bools = VecInit(values.map(c => (c == '-').B))
      bools(counter)
    }
  }
  val (a, b, c) = (WireInit(false.B), WireInit(false.B), WireInit(false.B))
  def seqs(reset: String, a: String, b: String, c: String = ""): Unit = {
    seqs_p(reset, a, b, c)
  }
  private def seqs_p(rStr: String, aStr: String, bStr: String, cStr: String): Unit = {
    val r = seq(rStr)
    withReset(false.B) {
      assume(reset.asBool === r)
    }
    if (aStr.nonEmpty) a := seq(aStr)
    if (bStr.nonEmpty) b := seq(bStr)

  }
}

object IfThen {
  def apply(if_clause: Bool, then_clause: Bool): Bool = !if_clause || then_clause
}


// SPDX-License-Identifier: Apache-2.0

package chiseltest.iotesters.examples

import scala.annotation.tailrec

object GCDCalculator {
  @tailrec
  def computeGcdResultsAndCycles(a: Int, b: Int, depth: Int = 1): (Int, Int) = {
    if (b == 0) {
      (a, depth)

    } else {
      computeGcdResultsAndCycles(b, a % b, depth + 1)
    }


// SPDX-License-Identifier: Apache-2.0

package chiseltest.iotesters.examples

import chisel3._
import chiseltest.iotesters._
import chisel3.util.Counter
import chiseltest.{ChiselScalatestTester, VerilatorBackendAnnotation}
import chiseltest.simulator.RequiresVerilator
import org.scalatest.freespec.AnyFreeSpec

class SecondClockDrivesRegisterSpec extends AnyFreeSpec with ChiselScalatestTester {
  class SecondClock extends Module {
    val inClock = IO(Input(Bool()))
    val out = IO(Output(UInt(8.W)))

    withClock(inClock.asClock) {
      out := Counter(true.B, 8)._1
    }
  }

  class SecondClockTester(c: SecondClock) extends PeekPokeTester(c) {
    poke(c.inClock, 0)
    expect(c.out, 0)

    // Main clock should do nothing
    step(1)
    expect(c.out, 0)
    step(1)
    expect(c.out, 0)

    // Output should advance on rising edge, even without main clock edge
    poke(c.inClock, 1)
    expect(c.out, 1)

    step(1)
    expect(c.out, 1)

    // Repeated, 1should do nothing
    poke(c.inClock, 1)
    expect(c.out, 1)

    // and again
    poke(c.inClock, 0)
    expect(c.out, 1)
    poke(c.inClock, 1)
    expect(c.out, 2)
  }

  "poking a clock should flip register" - {

      test(new SecondClock).withAnnotations(Seq(VerilatorBackendAnnotation)).runPeekPoke(new SecondClockTester(_))
    }
  }
}

    "should work with Treadle" in {
      test(new SecondClock).runPeekPoke(new SecondClockTester(_))
    }


// SPDX-License-Identifier: Apache-2.0

package chiseltest.simulator

import firrtl2.AnnotationSeq
import org.scalatest.Tag

import java.nio.ByteBuffer

/** tests the waveform dumping abilities of a simulator */
abstract class WaveformCompliance(sim: Simulator, tag: Tag = DefaultTag) extends ComplianceTest(sim, tag) {

  private val testSrc =
    """circuit Foo :
      |  module Child :
      |    input clock : Clock
      |    input reset : UInt<1>
      |    output io : { flip in : UInt<1>, out : UInt<1>}
      |
      |    reg r : UInt<1>, clock with :
      |      reset => (reset, UInt(0))
      |    r <= io.in
      |    io.out <= r
      |
      |  module Foo :
      |    input clock : Clock
      |    input reset : UInt<1>
      |    output io : { flip in : UInt<1>, out : UInt<1>}
      |
      |    node _io_out_T = not(io.in) @[main.scala 12:13]
      |    inst c of Child
      |    c.clock <= clock
      |    c.reset <= reset
      |    c.io.in <= _io_out_T @[main.scala 12:10]
      |    io.out <= c.io.out
      |""".stripMargin

  val waveformExtensions = Set("vcd", "txt", "fst", "vpd", "lxt", "lxt2", "fsdb")
  it should "not create a waveform file when no WriteWaveformAnnotation is provided" taggedAs (tag) in {
    performDutTest(Seq())
    val dumps = testDirFiles().filter(f => waveformExtensions.contains(f.last.split('.').last))
    assert(dumps.isEmpty, s"Found waveform dump files $dumps even though no WriteWaveformAnnotation was provided!")
  }

  sim.waveformFormats.foreach { anno =>
    val format = anno.format
    it should s"generate a $format waveform dump at the end of simulation" taggedAs (tag) in {
      performDutTest(Seq(anno))
      val dumpFile = targetDir / ("Foo." + format)
      checkWavedump(dumpFile, format)
    }
  }

  // performs some sanity checks on a given waveform
  private def checkWavedump(file: os.Path, format: String): Unit = {
    assert(os.exists(file) && os.isFile(file), s"Waveform dump $file does not exist!")

    format match {
      case "vcd" =>
        // the VCD format is a text file that will contain some keyword we can search for
        val txt = os.read(file)
        assert(txt.contains("$version"))
        assert(txt.contains("$scope"))
        assert(txt.contains("$enddefinitions"))
      case "lxt" =>
        // the LXT format has a characteristic header ID followed by the version number
        // lt_emit_u16(lt, LT_HDRID);    // #define LT_HDRID (0x0138)
        // lt_emit_u16(lt, LT_VERSION);  // #define LT_VERSION (0x0004)
        val bb = ByteBuffer.wrap(os.read.bytes(file))
        assert(bb.getShort == 0x0138)
        val version = bb.getShort
        assert(version == 4) // TODO: potentially relax this version requirement
      case "lxt2" =>
        // the LXT2 format has a characteristic header ID followed by the version number
        // lxt2_wr_emit_u16(lt, LXT2_WR_HDRID);   // #define LXT2_WR_HDRID (0x1380)
        // lxt2_wr_emit_u16(lt, LXT2_WR_VERSION); // #define LXT2_WR_VERSION (0x0001)
        // lxt2_wr_emit_u8 (lt, LXT2_WR_GRANULE_SIZE);	/* currently 32 or 64 */
        val bb = ByteBuffer.wrap(os.read.bytes(file))
        assert(bb.getShort == 0x1380)
        val version = bb.getShort
        val granule_size = bb.get()
        assert(granule_size == 32 || granule_size == 64)
        assert(version == 1) // TODO: potentially relax this version requirement
      case "fst" =>
        // we look for characteristic header bytes defined in `fstWriterEmitHdrBytes`
        val bb = ByteBuffer.wrap(os.read.bytes(file))
        // fstBlockType: FST_BL_HDR = 0
        assert(bb.get() == 0)
        // section length is hard coded to 329
        assert(bb.getLong() == 329)
      case "vpd" =>
        val txt = os.read(file).toString.take(3)
        assert(txt.startsWith("xV4"))
      case "fsdb" =>
        val txt = os.read(file)
        assert(txt.contains("FSDB Writer"))
        assert(txt.contains("finish"))
      case other => throw new NotImplementedError(s"TODO: add code to check $other")
    }
  }

  // perform some testing with the dut in order to generate interesting waveforms
  private def performDutTest(annos: AnnotationSeq): Unit = {
    val dut = load(testSrc, annos = annos)
    dut.poke("reset", 1)
    dut.step()
    dut.poke("reset", 0)
    assert(dut.peek("io_out") == 0)
    val rand = new scala.util.Random(0)
    (0 until 50).foreach { _ =>
      val in = BigInt(1, rand)
      dut.poke("io_in", in)
      dut.step()
      assert(dut.peek("io_out") == ((~in) & 1))
    }
    dut.step()
    dut.finish()
  }

private def testDirFiles(): Seq[os.Path] = os.list(targetDir).filter(os.isFile)


/***************************************************************************************
* Copyright (c) 2024 Beijing Institute of Open Source Chip (BOSC)
* Copyright (c) 2020-2024 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 top

import circt.stage._
import chisel3.stage.ChiselGeneratorAnnotation
import xiangshan.transforms._

object Generator {

  def execute(args: Array[String], mod: => chisel3.RawModule, firtoolOpts: Array[String]) = {
    val annotations = firtoolOpts.map(FirtoolOption.apply).toSeq

    (new XiangShanStage).execute(args, ChiselGeneratorAnnotation(() => mod) +: annotations)
  }


package xiangshan.backend.decode

import org.chipsalliance.cde.config.Parameters
import chisel3._
import chisel3.util._
import freechips.rocketchip.rocket.Instructions._
import freechips.rocketchip.util.uintToBitPat
import utility._
import utils._
import xiangshan._
import xiangshan.backend.Bundles.{DecodedInst, DynInst, StaticInst}
import xiangshan.backend.fu.FuType
import xiangshan.backend.fu.vector.Bundles._
import xiangshan.backend.decode.isa.bitfield.{InstVType, XSInstBitFields, OPCODE7Bit}
import xiangshan.backend.decode.Zvbb._

object RegNumNotAlign {

object NFtoLmul {
  def apply(nf: UInt): UInt = {
    LookupTree(nf, List(
      "b000".U -> 4.U,
      "b001".U -> 5.U,
      "b011".U -> 6.U,
      "b111".U -> 7.U
    ))
  }
}

object LmultoRegNum {
  def apply(lmul: UInt): UInt = {
    val numPow = Mux(lmul(2).asBool, lmul(1, 0), 0.U(2.W))
    val regNum = 1.U << numPow
    regNum
  }
}

class VecExceptionGen(implicit p: Parameters) extends XSModule{
  val io = IO(new Bundle(){
    val inst = Input(UInt(32.W))
    val decodedInst = Input(new DecodedInst)
    val vtype = Input(new VType)
    val vstart = Input(Vl())

    val illegalInst = Output(Bool())
  })

  private val inst: XSInstBitFields = io.inst.asTypeOf(new XSInstBitFields)
  private val isVArithMem = FuType.isVArithMem(io.decodedInst.fuType)
  private val isVArith = FuType.isVArith(io.decodedInst.fuType)
  private val isVset = FuType.isVset(io.decodedInst.fuType)

  private val SEW = io.vtype.vsew(1, 0)
  private val LMUL = Cat(~io.vtype.vlmul(2), io.vtype.vlmul(1, 0))

  private val lsStrideInst = Seq(
    VLE8_V, VLE16_V, VLE32_V, VLE64_V, VSE8_V, VSE16_V, VSE32_V, VSE64_V,
    VLSE8_V, VLSE16_V, VLSE32_V, VLSE64_V, VSSE8_V, VSSE16_V, VSSE32_V, VSSE64_V,
    VLE8FF_V, VLE16FF_V, VLE32FF_V, VLE64FF_V
  ).map(_ === inst.ALL).reduce(_ || _)

  private val lsMaskInst = Seq(
    VLM_V, VSM_V
  ).map(_ === inst.ALL).reduce(_ || _)

  private val lsIndexInst = Seq(
    VLUXEI8_V, VLUXEI16_V, VLUXEI32_V, VLUXEI64_V, VLOXEI8_V, VLOXEI16_V, VLOXEI32_V, VLOXEI64_V,
    VSUXEI8_V, VSUXEI16_V, VSUXEI32_V, VSUXEI64_V, VSOXEI8_V, VSOXEI16_V, VSOXEI32_V, VSOXEI64_V
  ).map(_ === inst.ALL).reduce(_ || _)

  private val lsWholeInst = Seq(
    VL1RE8_V, VL1RE16_V, VL1RE32_V, VL1RE64_V,
    VL2RE8_V, VL2RE16_V, VL2RE32_V, VL2RE64_V,
    VL4RE8_V, VL4RE16_V, VL4RE32_V, VL4RE64_V,
    VL8RE8_V, VL8RE16_V, VL8RE32_V, VL8RE64_V,
    VS1R_V, VS2R_V, VS4R_V, VS8R_V
  ).map(_ === inst.ALL).reduce(_ || _)

  private val vdWideningInst = Seq(
    //int
    VWADD_VV, VWADD_VX, VWADD_WV, VWADD_WX, VWADDU_VV, VWADDU_VX, VWADDU_WV, VWADDU_WX,
    VWMACC_VV, VWMACC_VX, VWMACCSU_VV, VWMACCSU_VX, VWMACCU_VV, VWMACCU_VX, VWMACCUS_VX,
    VWMUL_VV, VWMUL_VX, VWMULSU_VV, VWMULSU_VX, VWMULU_VV, VWMULU_VX,
    VWSUB_VV, VWSUB_VX, VWSUB_WV, VWSUB_WX, VWSUBU_VV, VWSUBU_VX, VWSUBU_WV, VWSUBU_WX,
    //fp
    VFWADD_VF, VFWADD_VV, VFWADD_WF, VFWADD_WV, VFWSUB_VF, VFWSUB_VV, VFWSUB_WF, VFWSUB_WV,
    VFWMUL_VF, VFWMUL_VV,
    VFWMACC_VF, VFWMACC_VV, VFWMSAC_VF, VFWMSAC_VV, VFWNMACC_VF, VFWNMACC_VV, VFWNMSAC_VF, VFWNMSAC_VV,
    VFWCVT_F_F_V, VFWCVT_F_X_V, VFWCVT_F_XU_V, VFWCVT_RTZ_X_F_V, VFWCVT_RTZ_XU_F_V, VFWCVT_X_F_V, VFWCVT_XU_F_V,
    // zvbb
    VWSLL_VV, VWSLL_VX, VWSLL_VI,
  ).map(_ === inst.ALL).reduce(_ || _)

  private val vs2WideningInst = Seq(
    //int
    VWADD_WV, VWADD_WX, VWADDU_WV, VWADDU_WX,
    VWSUB_WV, VWSUB_WX, VWSUBU_WV, VWSUBU_WX,
    //fp
    VFWADD_WF, VFWADD_WV, VFWSUB_WF, VFWSUB_WV
  ).map(_ === inst.ALL).reduce(_ || _)

  private val narrowingInst = Seq(
    //int
    VNCLIP_WI, VNCLIP_WV, VNCLIP_WX, VNCLIPU_WI, VNCLIPU_WV, VNCLIPU_WX,
    VNSRA_WI, VNSRA_WV, VNSRA_WX, VNSRL_WI, VNSRL_WV, VNSRL_WX,
    //fp
    VFNCVT_F_F_W, VFNCVT_F_X_W, VFNCVT_F_XU_W, VFNCVT_ROD_F_F_W, VFNCVT_RTZ_X_F_W, VFNCVT_RTZ_XU_F_W, VFNCVT_X_F_W, VFNCVT_XU_F_W
  ).map(_ === inst.ALL).reduce(_ || _)

  private val intExtInst = Seq(
    VSEXT_VF2, VSEXT_VF4, VSEXT_VF8, VZEXT_VF2, VZEXT_VF4, VZEXT_VF8
  ).map(_ === inst.ALL).reduce(_ || _)

  private val acsbInst = Seq(
    VMADC_VI, VMADC_VIM, VMADC_VV, VMADC_VVM, VMADC_VX, VMADC_VXM,
    VMSBC_VV, VMSBC_VVM, VMSBC_VX, VMSBC_VXM
  ).map(_ === inst.ALL).reduce(_ || _)

  private val cmpInst = Seq(
    //int
    VMSEQ_VI, VMSEQ_VV, VMSEQ_VX,
    VMSGT_VI, VMSGT_VX, VMSGTU_VI, VMSGTU_VX,
    VMSLE_VI, VMSLE_VV, VMSLE_VX, VMSLEU_VI, VMSLEU_VV, VMSLEU_VX,
    VMSLT_VV, VMSLT_VX, VMSLTU_VV, VMSLTU_VX,
    VMSNE_VI, VMSNE_VV, VMSNE_VX,
    //fp
    VMFEQ_VF, VMFEQ_VV, VMFNE_VF, VMFNE_VV,
    VMFGE_VF, VMFGT_VF, VMFLE_VF, VMFLE_VV, VMFLT_VF, VMFLT_VV
  ).map(_ === inst.ALL).reduce(_ || _)

  private val redInst = Seq(
    VREDAND_VS, VREDMAX_VS, VREDMAXU_VS, VREDMIN_VS, VREDMINU_VS, VREDOR_VS, VREDSUM_VS, VREDXOR_VS,
    VFREDMAX_VS, VFREDMIN_VS, VFREDOSUM_VS, VFREDUSUM_VS
  ).map(_ === inst.ALL).reduce(_ || _)

  private val redWideningInst = Seq(
    VWREDSUM_VS, VWREDSUMU_VS,
    VFWREDOSUM_VS, VFWREDUSUM_VS
  ).map(_ === inst.ALL).reduce(_ || _)

  private val maskLogicalInst = Seq(
    VMAND_MM, VMNAND_MM, VMANDN_MM, VMXOR_MM, VMOR_MM, VMNOR_MM, VMORN_MM, VMXNOR_MM
  ).map(_ === inst.ALL).reduce(_ || _)

  private val maskArithmeticInst = Seq(
    VCPOP_M, VFIRST_M, VMSBF_M, VMSIF_M, VMSOF_M
  ).map(_ === inst.ALL).reduce(_ || _) || maskLogicalInst

  private val maskIndexInst = Seq(
    VIOTA_M, VID_V
  ).map(_ === inst.ALL).reduce(_ || _)

  private val vmvSingleInst = Seq(
    VMV_X_S, VMV_S_X, VFMV_F_S, VFMV_S_F
  ).map(_ === inst.ALL).reduce(_ || _)

  private val vmvWholeInst = Seq(
    VMV1R_V, VMV2R_V, VMV4R_V, VMV8R_V
  ).map(_ === inst.ALL).reduce(_ || _)

  private val vrgather16 = VRGATHEREI16_VV === inst.ALL
  private val vcompress = VCOMPRESS_VM === inst.ALL
  private val intExt2 = Seq(VSEXT_VF2, VZEXT_VF2).map(_ === inst.ALL).reduce(_ || _)
  private val intExt4 = Seq(VSEXT_VF4, VZEXT_VF4).map(_ === inst.ALL).reduce(_ || _)
  private val intExt8 = Seq(VSEXT_VF8, VZEXT_VF8).map(_ === inst.ALL).reduce(_ || _)

  private val notDependVtypeInst = Seq(VSETVLI, VSETIVLI, VSETVL).map(_ === inst.ALL).reduce(_ || _) || lsWholeInst

  // 1. inst Illegal
  private val instIllegal = maskLogicalInst && inst.VM === 0.U

  // 2. vill Illegal
  private val villIllegal = io.vtype.illegal && isVArithMem && !notDependVtypeInst

  // 3. EEW Illegal
  private val doubleFpInst = Seq(
    VFWCVT_F_X_V, VFWCVT_F_XU_V, VFNCVT_RTZ_X_F_W, VFNCVT_RTZ_XU_F_W, VFNCVT_X_F_W, VFNCVT_XU_F_W
  ).map(_ === inst.ALL).reduce(_ || _)

  // funct3 of OPFVV is 001, funct3 of OPFVF is 101
  private val isFp = (inst.FUNCT3 === BitPat("b?01")) && (inst.OPCODE7Bit === OPCODE7Bit.VECTOR_ARITH)
  private val fpEewIllegal = isFp && ((SEW === 0.U) && !doubleFpInst)

  private val intExtEewIllegal = intExt2 && SEW === 0.U ||
                                 intExt4 && SEW <= 1.U ||
                                 intExt8 && SEW <= 2.U

  private val wnEewIllegal = (vdWideningInst || narrowingInst || redWideningInst) && SEW === 3.U

  private val eewIllegal = fpEewIllegal || intExtEewIllegal || wnEewIllegal

  // 4. EMUL Illegal
  private val lsEmulIllegal = (lsStrideInst || lsIndexInst) && (LMUL +& inst.WIDTH(1, 0) < SEW +& 1.U || LMUL +& inst.WIDTH(1, 0) > SEW +& 7.U)

  private val intExtEmulIllegal = intExt2 && LMUL === 1.U ||
                                  intExt4 && LMUL <= 2.U ||
                                  intExt8 && LMUL <= 3.U

  private val wnEmulIllegal = (vdWideningInst || narrowingInst) && LMUL === 7.U

  private val gather16EmulIllegal = vrgather16 && (LMUL < SEW || LMUL > SEW +& 6.U)

  private val NFIELDS = inst.NF +& 1.U
  private val segEmul = Mux(lsIndexInst, LMUL, LMUL +& inst.WIDTH(1, 0) - SEW)
  private val emulNumPow = Mux(segEmul(2), segEmul(1, 0), 0.U(2.W))
  private val segRegNum = NFIELDS << emulNumPow
  private val segRegMax = inst.VD +& segRegNum

  private val lsSegIllegal = (lsStrideInst || lsIndexInst) && inst.NF =/= 0.U && (segRegNum > 8.U || segRegMax > 32.U)

  private val emulIllegal = lsEmulIllegal || intExtEmulIllegal || wnEmulIllegal || gather16EmulIllegal || lsSegIllegal

  // 5. Reg Number Align
  private val vs1IsMask = maskArithmeticInst || vcompress
  private val vs1IsSingleElem = redInst || redWideningInst
  private val vs1Eew = Mux(vrgather16, "b01".U, SEW)
  private val vs1Emul = Mux(vs1IsMask || vs1IsSingleElem, "b100".U, Mux(vrgather16, LMUL +& 1.U - SEW, LMUL))
  private val vs1NotAlign = SrcType.isVp(io.decodedInst.srcType(0)) && RegNumNotAlign(inst.VS1, vs1Emul)

  private val vs2IsMask = maskArithmeticInst || maskIndexInst
  private val vs2IsSingleElem = vmvSingleInst
  private val vs2EewSel = Cat(lsIndexInst, (vs2WideningInst || narrowingInst || redWideningInst), intExt2, intExt4, intExt8)
  private val vs2Eew = LookupTreeDefault(vs2EewSel, SEW, List(
    "b10000".U  -> inst.WIDTH(1, 0),
    "b01000".U  -> (SEW + 1.U),
    "b00100".U  -> (SEW - 1.U),
    "b00010".U  -> (SEW - 2.U),
    "b00001".U  -> (SEW - 3.U)
  ))
  private val vs2EmulSel = Cat((vs2IsMask || vs2IsSingleElem), (vs2WideningInst || narrowingInst), vmvWholeInst, (intExtInst || lsIndexInst))
  private val vs2Emul = LookupTreeDefault(vs2EmulSel, LMUL, List(
    "b1000".U  -> "b100".U,
    "b0100".U  -> (LMUL + 1.U),
    "b0010".U  -> NFtoLmul(inst.IMM5_OPIVI(2, 0)),
    "b0001".U  -> (LMUL +& vs2Eew - SEW)
  ))
  private val vs2NotAlign = SrcType.isVp(io.decodedInst.srcType(1)) && RegNumNotAlign(inst.VS2, vs2Emul)

  private val vdIsMask = lsMaskInst || acsbInst || cmpInst || maskArithmeticInst
  private val vdIsSingleElem = redInst || redWideningInst || vmvSingleInst
  private val vdEew = Mux(lsStrideInst, inst.WIDTH(1, 0), Mux(vdWideningInst || redWideningInst, SEW + 1.U, SEW))
  private val vdEmulSel = Cat((vdIsMask || vdIsSingleElem), vdWideningInst, vmvWholeInst, lsWholeInst, lsStrideInst)
  private val vdEmul = LookupTreeDefault(vdEmulSel, LMUL, List(
    "b10000".U  -> "b100".U,
    "b01000".U  -> (LMUL + 1.U),
    "b00100".U  -> NFtoLmul(inst.IMM5_OPIVI(2, 0)),
    "b00010".U  -> NFtoLmul(inst.NF),
    "b00001".U  -> (LMUL +& vdEew - SEW)
  ))
  private val vdNotAlign = (SrcType.isVp(io.decodedInst.srcType(2)) || io.decodedInst.vecWen) && RegNumNotAlign(inst.VD, vdEmul)

  private val regNumIllegal = isVArithMem && (vs1NotAlign || vs2NotAlign || vdNotAlign)

  // 6. v0 Overlap
  private val v0AllowOverlap = (vdIsMask || vdIsSingleElem) && !Seq(VMSBF_M, VMSIF_M, VMSOF_M).map(_ === inst.ALL).reduce(_ || _)
  private val v0Overlap = isVArithMem && io.decodedInst.vecWen && inst.VM === 0.U && inst.VD === 0.U && !v0AllowOverlap

  // 7. Src Reg Overlap
  private val vs1RegLo = inst.VS1
  private val vs1RegHi = inst.VS1 +& LmultoRegNum(vs1Emul) - 1.U
  private val vs2RegLo = inst.VS2
  private val vs2RegHi = inst.VS2 +& LmultoRegNum(vs2Emul) - 1.U
  private val vdRegLo = inst.VD
  private val vdRegHi = Mux(lsStrideInst || lsIndexInst, segRegMax - 1.U, inst.VD + LmultoRegNum(vdEmul) - 1.U)

  private val notAllowOverlapInst = lsIndexInst && inst.NF =/= 0.U || Seq(VMSBF_M, VMSIF_M, VMSOF_M, VIOTA_M,
                                    VSLIDEUP_VX, VSLIDEUP_VI, VSLIDE1UP_VX, VFSLIDE1UP_VF, VRGATHER_VV, VRGATHEREI16_VV, VRGATHER_VX, VRGATHER_VI, VCOMPRESS_VM).map(_ === inst.ALL).reduce(_ || _)

  // 8. vstart Illegal
  private val vstartIllegal = isVArith && (io.vstart =/= 0.U)

  //vs1
  private val vs1vdRegNotOverlap = vs1RegHi < vdRegLo || vdRegHi < vs1RegLo
  private val vs1Constraint1 = vs1IsMask && vdIsMask || !vs1IsMask && !vdIsMask && vs1Eew === vdEew
  private val vs1Constraint2 = (vdIsMask && !vs1IsMask || !vs1IsMask && !vdIsMask && vs1Eew > vdEew) && vdRegLo === vs1RegLo && vdRegHi <= vs1RegHi
  private val vs1Constraint3 = (!vdIsMask && vs1IsMask || !vs1IsMask && !vdIsMask && vs1Eew < vdEew) && vs1Emul >= "b100".U && vdRegHi === vs1RegHi && vdRegLo <= vs1RegLo
  private val vs1AllowOverlap = (vs1Constraint1 || vs1Constraint2 || vs1Constraint3 || vdIsSingleElem) && !notAllowOverlapInst
  private val vs1vdOverlap = (SrcType.isVp(io.decodedInst.srcType(0)) && io.decodedInst.vecWen) && !vs1vdRegNotOverlap && !vs1AllowOverlap
  //vs2
  private val vs2vdRegNotOverlap = vs2RegHi < vdRegLo || vdRegHi < vs2RegLo
  private val vs2Constraint1 = vs2IsMask && vdIsMask || !vs2IsMask && !vdIsMask && vs2Eew === vdEew
  private val vs2Constraint2 = (vdIsMask && !vs2IsMask || !vs2IsMask && !vdIsMask && vs2Eew > vdEew) && vdRegLo === vs2RegLo && vdRegHi <= vs2RegHi
  private val vs2Constraint3 = (!vdIsMask && vs2IsMask || !vs2IsMask && !vdIsMask && vs2Eew < vdEew) && vs2Emul >= "b100".U && vdRegHi === vs2RegHi && vdRegLo <= vs2RegLo
  private val vs2AllowOverlap = (vs2Constraint1 || vs2Constraint2 || vs2Constraint3 || vdIsSingleElem) && !notAllowOverlapInst
  private val vs2vdOverlap = (SrcType.isVp(io.decodedInst.srcType(1)) && io.decodedInst.vecWen) && !vs2vdRegNotOverlap && !vs2AllowOverlap

  private val regOverlapIllegal = v0Overlap || vs1vdOverlap || vs2vdOverlap

  io.illegalInst := instIllegal || villIllegal || eewIllegal || emulIllegal || regNumIllegal || regOverlapIllegal || vstartIllegal
  dontTouch(instIllegal)
  dontTouch(villIllegal)
  dontTouch(eewIllegal)
  dontTouch(emulIllegal)
  dontTouch(regNumIllegal)
  dontTouch(regOverlapIllegal)
  dontTouch(notDependVtypeInst)
  dontTouch(vstartIllegal)
}


/***************************************************************************************
* 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.backend.fu

import org.chipsalliance.cde.config.Parameters
import chisel3._
import chisel3.util._
import utility.{LookupTree, LookupTreeDefault, ParallelMux, SignExt, XSDebug, ZeroExt}
import xiangshan._

class AddModule(implicit p: Parameters) extends XSModule {
  val io = IO(new Bundle() {
    val src = Vec(2, Input(UInt(XLEN.W)))
    val srcw = Input(UInt((XLEN/2).W))
    val add = Output(UInt(XLEN.W))
    val addw = Output(UInt((XLEN/2).W))
  })
  io.add := io.src(0) + io.src(1)
  // TODO: why this extra adder?
  io.addw := io.srcw + io.src(1)(31,0)
}

class SubModule(implicit p: Parameters) extends XSModule {
  val io = IO(new Bundle() {
    val src = Vec(2, Input(UInt(XLEN.W)))
    val sub = Output(UInt((XLEN+1).W))
  })
  io.sub := (io.src(0) +& (~io.src(1)).asUInt) + 1.U
}

class LeftShiftModule(implicit p: Parameters) extends XSModule {
  val io = IO(new Bundle() {
    val shamt = Input(UInt(6.W))
    val revShamt = Input(UInt(6.W))
    val sllSrc = Input(UInt(XLEN.W))
    val sll = Output(UInt(XLEN.W))
    val revSll = Output(UInt(XLEN.W))
  })
  io.sll := io.sllSrc << io.shamt
  io.revSll := io.sllSrc << io.revShamt
}

class LeftShiftWordModule(implicit p: Parameters) extends XSModule {
  val io = IO(new Bundle() {
    val shamt = Input(UInt(5.W))
    val revShamt = Input(UInt(5.W))
    val sllSrc = Input(UInt((XLEN/2).W))
    val sllw = Output(UInt((XLEN/2).W))
    val revSllw = Output(UInt((XLEN/2).W))
  })
  io.sllw := io.sllSrc << io.shamt
  io.revSllw := io.sllSrc << io.revShamt
}

class RightShiftModule(implicit p: Parameters) extends XSModule {
  val io = IO(new Bundle() {
    val shamt = Input(UInt(6.W))
    val revShamt = Input(UInt(6.W))
    val srlSrc, sraSrc = Input(UInt(XLEN.W))
    val srl, sra = Output(UInt(XLEN.W))
    val revSrl = Output(UInt(XLEN.W))
  })
  io.srl  := io.srlSrc >> io.shamt
  io.sra  := (io.sraSrc.asSInt >> io.shamt).asUInt
  io.revSrl  := io.srlSrc >> io.revShamt
}

class RightShiftWordModule(implicit p: Parameters) extends XSModule {
  val io = IO(new Bundle() {
    val shamt = Input(UInt(5.W))
    val revShamt = Input(UInt(5.W))
    val srlSrc, sraSrc = Input(UInt((XLEN/2).W))
    val srlw, sraw = Output(UInt((XLEN/2).W))
    val revSrlw = Output(UInt((XLEN/2).W))
  })

  io.srlw := io.srlSrc >> io.shamt
  io.sraw := (io.sraSrc.asSInt >> io.shamt).asUInt
  io.revSrlw := io.srlSrc >> io.revShamt
}

class MiscResultSelect(implicit p: Parameters) extends XSModule {
  val io = IO(new Bundle() {
    val func = Input(UInt(6.W))
    val and, or, xor, orcb, orh48, sextb, packh, sexth, packw, revb, rev8, pack = Input(UInt(XLEN.W))
    val src = Input(UInt(XLEN.W))
    val miscRes = Output(UInt(XLEN.W))
  })

  val logicRes = VecInit(Seq(
    io.and,
    io.or,
    io.xor,
    io.orcb
  ))(io.func(2, 1))
  val miscRes = VecInit(Seq(io.sextb, io.packh, io.sexth, io.packw))(io.func(1, 0))
  val logicBase = Mux(io.func(3), miscRes, logicRes)

  val revRes = VecInit(Seq(io.revb, io.rev8, io.pack, io.orh48))(io.func(1, 0))
  val customRes = VecInit(Seq(
    Cat(0.U(31.W), io.src(31, 0), 0.U(1.W)),
    Cat(0.U(30.W), io.src(31, 0), 0.U(2.W)),
    Cat(0.U(29.W), io.src(31, 0), 0.U(3.W)),
    Cat(0.U(56.W), io.src(15, 8))))(io.func(1, 0))
  val logicAdv = Mux(io.func(3), customRes, revRes)

  val mask = Cat(Fill(15, io.func(0)), 1.U(1.W))
  val maskedLogicRes = mask & logicRes

  io.miscRes := Mux(io.func(5), maskedLogicRes, Mux(io.func(4), logicAdv, logicBase))
}

class ConditionalZeroModule(implicit p: Parameters) extends XSModule {
  val io = IO(new Bundle() {
    val condition = Input(UInt(XLEN.W))
    val value = Input(UInt(XLEN.W))
    val isNez = Input(Bool())
    val condRes = Output(UInt(XLEN.W))
  })

  val condition_zero = io.condition === 0.U
  val use_zero = !io.isNez &&  condition_zero ||
                  io.isNez && !condition_zero

  io.condRes := Mux(use_zero, 0.U, io.value)
}

class ShiftResultSelect(implicit p: Parameters) extends XSModule {
  val io = IO(new Bundle() {
    val func = Input(UInt(4.W))
    val sll, srl, sra, rol, ror, bclr, bset, binv, bext = Input(UInt(XLEN.W))
    val shiftRes = Output(UInt(XLEN.W))
  })

  // val leftBit  = Mux(io.func(1), io.binv, Mux(io.func(0), io.bset, io.bclr))
  // val leftRes  = Mux(io.func(2), leftBit, io.sll)
  // val rightRes = Mux(io.func(1) && io.func(0), io.sra, Mux(io.func(1), io.bext, io.srl))
  val resultSource = VecInit(Seq(
    io.sll,
    io.sll,
    io.bclr,
    io.bset,
    io.binv,
    io.srl,
    io.bext,
    io.sra
  ))
  val simple = resultSource(io.func(2, 0))

  io.shiftRes := Mux(io.func(3), Mux(io.func(1), io.ror, io.rol), simple)
}

class WordResultSelect(implicit p: Parameters) extends XSModule {
  val io = IO(new Bundle() {
    val func = Input(UInt())
    val sllw, srlw, sraw, rolw, rorw, addw, subw = Input(UInt((XLEN/2).W))
    val wordRes = Output(UInt(XLEN.W))
  })

  val addsubRes = Mux(!io.func(2) && io.func(1) && !io.func(0), io.subw, io.addw)
  val shiftRes = Mux(io.func(2), Mux(io.func(0), io.rorw, io.rolw),
                  Mux(io.func(1), io.sraw, Mux(io.func(0), io.srlw, io.sllw)))
  val wordRes = Mux(io.func(3), shiftRes, addsubRes)
  io.wordRes := SignExt(wordRes, XLEN)
}

class AluResSel(implicit p: Parameters) extends XSModule {
  val io = IO(new Bundle() {
    val func = Input(UInt(3.W))
    val addRes, shiftRes, miscRes, compareRes, wordRes, condRes = Input(UInt(XLEN.W))
    val aluRes = Output(UInt(XLEN.W))
  })

  val res = Mux(io.func(2, 1) === 0.U, Mux(io.func(0), io.wordRes, io.shiftRes),
                                       Mux(!io.func(2), Mux(io.func(0), io.compareRes, io.addRes),
                                                        Mux(io.func(1, 0) === 3.U, io.condRes, io.miscRes)))
  io.aluRes := res
}

class AluDataModule(implicit p: Parameters) extends XSModule {
  val io = IO(new Bundle() {
    val src = Vec(2, Input(UInt(XLEN.W)))
    val func = Input(FuOpType())
    val result = Output(UInt(XLEN.W))
  })
  val (src1, src2, func) = (io.src(0), io.src(1), io.func)

  val shamt = src2(5, 0)
  val revShamt = ~src2(5,0) + 1.U

  // slliuw, sll
  val leftShiftModule = Module(new LeftShiftModule)
  val sll = leftShiftModule.io.sll
  val revSll = leftShiftModule.io.revSll
  leftShiftModule.io.sllSrc := Cat(Fill(32, func(0)), Fill(32, 1.U)) & src1
  leftShiftModule.io.shamt := shamt
  leftShiftModule.io.revShamt := revShamt

  // bclr, bset, binv
  val bitShift = 1.U << src2(5, 0)
  val bclr = src1 & ~bitShift
  val bset = src1 | bitShift
  val binv = src1 ^ bitShift

  // srl, sra, bext
  val rightShiftModule = Module(new RightShiftModule)
  val srl = rightShiftModule.io.srl
  val revSrl = rightShiftModule.io.revSrl
  val sra = rightShiftModule.io.sra
  rightShiftModule.io.shamt := shamt
  rightShiftModule.io.revShamt := revShamt
  rightShiftModule.io.srlSrc := src1
  rightShiftModule.io.sraSrc := src1
  val bext = srl(0)

  val rol = revSrl | sll
  val ror = srl | revSll

  // addw
  val addModule = Module(new AddModule)
  addModule.io.srcw := Mux(!func(2) && func(0), Mux(func(1), SignExt(src2(11, 0), XLEN), ZeroExt(src1(0), XLEN)), src1(31, 0))
  val addwResultAll = VecInit(Seq(
    ZeroExt(addModule.io.addw(0), XLEN),
    ZeroExt(addModule.io.addw(7, 0), XLEN),
    ZeroExt(addModule.io.addw(15, 0), XLEN),
    SignExt(addModule.io.addw(15, 0), XLEN)
  ))
  val addw = Mux(func(2), addwResultAll(func(1, 0)), addModule.io.addw)

  // subw
  val subModule = Module(new SubModule)
  val subw = subModule.io.sub

  // sllw
  val leftShiftWordModule = Module(new LeftShiftWordModule)
  val sllw = leftShiftWordModule.io.sllw
  val revSllw = leftShiftWordModule.io.revSllw
  leftShiftWordModule.io.sllSrc := src1
  leftShiftWordModule.io.shamt := shamt
  leftShiftWordModule.io.revShamt := revShamt

  val rightShiftWordModule = Module(new RightShiftWordModule)
  val srlw = rightShiftWordModule.io.srlw
  val revSrlw = rightShiftWordModule.io.revSrlw
  val sraw = rightShiftWordModule.io.sraw
  rightShiftWordModule.io.shamt := shamt
  rightShiftWordModule.io.revShamt := revShamt
  rightShiftWordModule.io.srlSrc := src1
  rightShiftWordModule.io.sraSrc := src1

  val rolw = revSrlw | sllw
  val rorw = srlw | revSllw

  // add
  val wordMaskAddSource = Cat(Fill(32, func(0)), Fill(32, 1.U)) & src1
  val shaddSource = VecInit(Seq(
    Cat(wordMaskAddSource(62, 0), 0.U(1.W)),
    Cat(wordMaskAddSource(61, 0), 0.U(2.W)),
    Cat(wordMaskAddSource(60, 0), 0.U(3.W)),
    Cat(wordMaskAddSource(59, 0), 0.U(4.W))
  ))
  val sraddSource = VecInit(Seq(
    ZeroExt(src1(63, 29), XLEN),
    ZeroExt(src1(63, 30), XLEN),
    ZeroExt(src1(63, 31), XLEN),
    ZeroExt(src1(63, 32), XLEN)
  ))
  // TODO: use decoder or other libraries to optimize timing
  // Now we assume shadd has the worst timing.
  addModule.io.src(0) := Mux(func(3), shaddSource(func(2, 1)),
    Mux(func(2), sraddSource(func(1, 0)),
    Mux(func(1), Mux(func(0), SignExt(src2(11, 0), XLEN), ZeroExt(src1(0), XLEN)), wordMaskAddSource))
  )
  addModule.io.src(1) := Mux(func(3, 0) === "b0011".U, Cat(src2(63, 12), 0.U(12.W)), src2)
  val add = addModule.io.add

  // sub
  val sub  = subModule.io.sub
  subModule.io.src(0) := src1
  subModule.io.src(1) := src2
  val sltu    = !sub(XLEN)
  val slt     = src1(XLEN - 1) ^ src2(XLEN - 1) ^ sltu
  val maxMin  = Mux(slt ^ func(0), src2, src1)
  val maxMinU = Mux(sltu ^ func(0), src2, src1)
  val compareRes = Mux(func(2), Mux(func(1), maxMin, maxMinU), Mux(func(1), slt, Mux(func(0), sltu, sub)))

  // logic
  val logicSrc2 = Mux(!func(5) && func(0), ~src2, src2)
  val and     = src1 & logicSrc2
  val or      = src1 | logicSrc2
  val xor     = src1 ^ logicSrc2
  val orcb    = Cat((0 until 8).map(i => Fill(8, src1(i * 8 + 7, i * 8).orR)).reverse)
  val orh48   = Cat(src1(63, 8), 0.U(8.W)) | src2

  val sextb = SignExt(src1(7, 0), XLEN)
  val packh = Cat(src2(7,0), src1(7,0))
  val sexth = SignExt(src1(15, 0), XLEN)
  val packw = SignExt(Cat(src2(15, 0), src1(15, 0)), XLEN)

  val revb = Cat((0 until 8).map(i => Reverse(src1(8 * i + 7, 8 * i))).reverse)
  val pack = Cat(src2(31, 0), src1(31, 0))
  val rev8 = Cat((0 until 8).map(i => src1(8 * i + 7, 8 * i)))

  // Result Select
  val shiftResSel = Module(new ShiftResultSelect)
  shiftResSel.io.func := func(3, 0)
  shiftResSel.io.sll  := sll
  shiftResSel.io.srl  := srl
  shiftResSel.io.sra  := sra
  shiftResSel.io.rol  := rol
  shiftResSel.io.ror  := ror
  shiftResSel.io.bclr := bclr
  shiftResSel.io.binv := binv
  shiftResSel.io.bset := bset
  shiftResSel.io.bext := bext
  val shiftRes = shiftResSel.io.shiftRes

  val miscResSel = Module(new MiscResultSelect)
  miscResSel.io.func    := func(5, 0)
  miscResSel.io.and     := and
  miscResSel.io.or      := or
  miscResSel.io.xor     := xor
  miscResSel.io.orcb    := orcb
  miscResSel.io.orh48   := orh48
  miscResSel.io.sextb   := sextb
  miscResSel.io.packh   := packh
  miscResSel.io.sexth   := sexth
  miscResSel.io.packw   := packw
  miscResSel.io.revb    := revb
  miscResSel.io.rev8    := rev8
  miscResSel.io.pack    := pack
  miscResSel.io.src     := src1
  val miscRes = miscResSel.io.miscRes

  val condModule = Module(new ConditionalZeroModule)
  condModule.io.value     := src1
  condModule.io.condition := src2
  condModule.io.isNez     := func(1)
  val condRes = condModule.io.condRes

  val wordResSel = Module(new WordResultSelect)
  wordResSel.io.func := func
  wordResSel.io.addw := addw
  wordResSel.io.subw := subw
  wordResSel.io.sllw := sllw
  wordResSel.io.srlw := srlw
  wordResSel.io.sraw := sraw
  wordResSel.io.rolw := rolw
  wordResSel.io.rorw := rorw
  val wordRes = wordResSel.io.wordRes

  val aluResSel = Module(new AluResSel)
  aluResSel.io.func := func(6, 4)
  aluResSel.io.addRes := add
  aluResSel.io.compareRes := compareRes
  aluResSel.io.shiftRes := shiftRes
  aluResSel.io.miscRes := miscRes
  aluResSel.io.wordRes := wordRes
  aluResSel.io.condRes := condRes
  val aluRes = aluResSel.io.aluRes

  io.result := aluRes

  XSDebug(func === ALUOpType.lui32add, p"[alu] func lui32: src1=${Hexadecimal(src1)} src2=${Hexadecimal(src2)} alures=${Hexadecimal(aluRes)}\n")
  XSDebug(func === ALUOpType.lui32add, p"[alu] func lui32: add_src1=${Hexadecimal(addModule.io.src(0))} add_src2=${Hexadecimal(addModule.io.src(1))} addres=${Hexadecimal(add)}\n")

  XSDebug(func === ALUOpType.lui32addw, p"[alu] func lui32w: src1=${Hexadecimal(src1)} src2=${Hexadecimal(src2)} alures=${Hexadecimal(aluRes)}\n")

XSDebug(func === ALUOpType.lui32addw, p"[alu] func lui32w: add_src1=${Hexadecimal(addModule.io.srcw)} add_src2=${Hexadecimal(addModule.io.src(1)(31,0))} addres=${Hexadecimal(addw)}\n")


package xiangshan.backend.fu.NewCSR

import chisel3._
import xiangshan.backend.fu.NewCSR.CSRFunc._

import scala.language.experimental.macros
import scala.reflect.runtime.{universe => ru}

object CSRDefines {
  object CSRField1Bits extends CSREnum with CSRMacroApply

  object CSRField2Bits extends CSREnum with CSRMacroApply

  object CSRField3Bits extends CSREnum with CSRMacroApply

  object CSRField4Bits extends CSREnum with CSRMacroApply

  object CSRField5Bits extends CSREnum with CSRMacroApply

  object CSRField6Bits extends CSREnum with CSRMacroApply

  object CSRField7Bits extends CSREnum with CSRMacroApply

  object CSRField8Bits extends CSREnum with CSRMacroApply

  object CSRField9Bits extends CSREnum with CSRMacroApply

  object CSRField10Bits extends CSREnum with CSRMacroApply

  object CSRField11Bits extends CSREnum with CSRMacroApply

  object CSRField12Bits extends CSREnum with CSRMacroApply

  object CSRField13Bits extends CSREnum with CSRMacroApply

  object CSRField14Bits extends CSREnum with CSRMacroApply

  object CSRField15Bits extends CSREnum with CSRMacroApply

  object CSRField16Bits extends CSREnum with CSRMacroApply

  object CSRField17Bits extends CSREnum with CSRMacroApply

  object CSRField18Bits extends CSREnum with CSRMacroApply

  object CSRField19Bits extends CSREnum with CSRMacroApply

  object CSRField20Bits extends CSREnum with CSRMacroApply

  object CSRField21Bits extends CSREnum with CSRMacroApply

  object CSRField22Bits extends CSREnum with CSRMacroApply

  object CSRField23Bits extends CSREnum with CSRMacroApply

  object CSRField24Bits extends CSREnum with CSRMacroApply

  object CSRField25Bits extends CSREnum with CSRMacroApply

  object CSRField26Bits extends CSREnum with CSRMacroApply

  object CSRField27Bits extends CSREnum with CSRMacroApply

  object CSRField28Bits extends CSREnum with CSRMacroApply

  object CSRField29Bits extends CSREnum with CSRMacroApply

  object CSRField30Bits extends CSREnum with CSRMacroApply

  object CSRField31Bits extends CSREnum with CSRMacroApply

  object CSRField32Bits extends CSREnum with CSRMacroApply

  object CSRField33Bits extends CSREnum with CSRMacroApply

  object CSRField34Bits extends CSREnum with CSRMacroApply

  object CSRField35Bits extends CSREnum with CSRMacroApply

  object CSRField36Bits extends CSREnum with CSRMacroApply

  object CSRField37Bits extends CSREnum with CSRMacroApply

  object CSRField38Bits extends CSREnum with CSRMacroApply

  object CSRField39Bits extends CSREnum with CSRMacroApply

  object CSRField40Bits extends CSREnum with CSRMacroApply

  object CSRField41Bits extends CSREnum with CSRMacroApply

  object CSRField42Bits extends CSREnum with CSRMacroApply

  object CSRField43Bits extends CSREnum with CSRMacroApply

  object CSRField44Bits extends CSREnum with CSRMacroApply

  object CSRField45Bits extends CSREnum with CSRMacroApply

  object CSRField46Bits extends CSREnum with CSRMacroApply

  object CSRField47Bits extends CSREnum with CSRMacroApply

  object CSRField48Bits extends CSREnum with CSRMacroApply

  object CSRField49Bits extends CSREnum with CSRMacroApply

  object CSRField50Bits extends CSREnum with CSRMacroApply

  object CSRField51Bits extends CSREnum with CSRMacroApply

  object CSRField52Bits extends CSREnum with CSRMacroApply

  object CSRField53Bits extends CSREnum with CSRMacroApply

  object CSRField54Bits extends CSREnum with CSRMacroApply

  object CSRField56Bits extends CSREnum with CSRMacroApply

  object CSRField57Bits extends CSREnum with CSRMacroApply

  object CSRField58Bits extends CSREnum with CSRMacroApply

  object CSRField59Bits extends CSREnum with CSRMacroApply

  object CSRField60Bits extends CSREnum with CSRMacroApply

  object CSRField61Bits extends CSREnum with CSRMacroApply

  object CSRField62Bits extends CSREnum with CSRMacroApply

  object CSRField63Bits extends CSREnum with CSRMacroApply

  object CSRField64Bits extends CSREnum with CSRMacroApply

  object ContextStatus extends CSREnum with ContextStatusDef with RWApply
  object ContextStatusRO extends CSREnum with ContextStatusDef with ROApply
  trait ContextStatusDef { this: CSREnum =>
    val Off = Value(0.U)
    val Initial = Value(1.U)
    val Clean = Value(2.U)
    val Dirty = Value(3.U)
  }

  object BMAField extends CSREnum with WARLApply {
    val ResetBMA = Value(0.U)
    val TestBMA = Value("h4000000".U)
  }

  object XLENField extends CSREnum with ROApply {
    val XLEN32 = Value(1.U)
    val XLEN64 = Value(2.U)
    val XLEN128 = Value(3.U)
  }

  object XtvecMode extends CSREnum with WARLApply {
    val Direct = Value(0.U)
    val Vectored = Value(1.U)

    override def isLegal(enumeration: CSREnumType): Bool = enumeration.isOneOf(Direct, Vectored)
  }

  object SatpMode extends CSREnum with WARLApply {
    val Bare = Value(0.U)
    val Sv39 = Value(8.U)
    val Sv48 = Value(9.U)
    val Sv57 = Value(10.U)
    val Sv64 = Value(11.U) // Reserved for page-based 64-bit virtual addressing

    // XiangShan only support Sv39 & Sv48 Page
    override def isLegal(enumeration: CSREnumType): Bool = enumeration.isOneOf(Bare, Sv39, Sv48)
  }

  object HgatpMode extends CSREnum with WARLApply {
    val Bare   = Value(0.U)
    val Sv39x4 = Value(8.U)
    val Sv48x4 = Value(9.U)
    val Sv57x4 = Value(10.U)

    // XiangShan only support Sv39 & Sv48 Page
    override def isLegal(enumeration: CSREnumType): Bool = enumeration.isOneOf(Bare, Sv39x4, Sv48x4)
  }

  object EnvCBIE extends CSREnum with WARLApply {
    val Off   = Value("b00".U)
    val Flush = Value("b01".U)
    val Inval = Value("b11".U)

    override def isLegal(enumeration: CSREnumType): Bool = enumeration.isOneOf(Off, Flush, Inval)
  }

  object EnvPMM extends CSREnum with WARLApply {
    val Disable  = Value("b00".U)
    val PMLEN7   = Value("b10".U)
    val PMLEN16  = Value("b11".U)

    override def isLegal(enumeration: CSREnumType): Bool = enumeration.isOneOf(Disable, PMLEN7, PMLEN16)
  }

  object ReflectHelper {
    val mirror: ru.Mirror = ru.runtimeMirror(getClass.getClassLoader)

    def getCSRFieldMethodMirror(typeString: String, msb: Int, lsb: Int): ru.MethodMirror = {
      val moduleSymbol = mirror.typeOf[CSRDefines.type].termSymbol
        .info.decl(ru.TermName(s"CSRField${msb - lsb + 1}Bits")).asModule

      val methodSymbol = moduleSymbol
        .info.member(ru.TermName(typeString)).asMethod

      val instanceMirror: ru.InstanceMirror = mirror.reflect(mirror.reflectModule(moduleSymbol).instance)
      val methodMirror: ru.MethodMirror = instanceMirror.reflectMethod(methodSymbol)

      methodMirror
    }
  }

  object CSRWARLField {
    private def helper(msb: Int, lsb: Int, wfn: CSRWfnType, rfn: CSRRfnType): CSREnumType = {
      val methodMirror = ReflectHelper.getCSRFieldMethodMirror("WARL", msb, lsb)
      methodMirror.apply(msb, lsb, wfn, rfn).asInstanceOf[CSREnumType]
    }

    def apply(msb: Int, lsb: Int, fn: CSRRfnType): CSREnumType = this.helper(msb, lsb, null, fn)

    def apply(bit: Int, fn: CSRRfnType): CSREnumType = this.helper(bit, bit, null, fn)

    def apply(msb: Int, lsb: Int, fn: CSRWfnType): CSREnumType = this.helper(msb, lsb, fn, null)

    def apply(bit: Int, fn: CSRWfnType): CSREnumType = this.helper(bit, bit, fn, null)
  }

  object CSRROField {
    private def helper(msb: Int, lsb: Int, rfn: CSRRfnType): CSREnumType = {
      val methodMirror = ReflectHelper.getCSRFieldMethodMirror("RO", msb, lsb)
      methodMirror.apply(msb, lsb, rfn).asInstanceOf[CSREnumType]
    }

    def apply(msb: Int, lsb: Int, rfn: CSRRfnType): CSREnumType = this.helper(msb, lsb, rfn)

    def apply(bit: Int, rfn: CSRRfnType): CSREnumType = this.helper(bit, bit, rfn)

    def apply(msb: Int, lsb: Int): CSREnumType = this.helper(msb, lsb, null)

    def apply(bit: Int): CSREnumType = this.helper(bit, bit, null)
  }

  object CSRRWField {
    private def helper(msb: Int, lsb: Int) : CSREnumType = {
      val methodMirror: ru.MethodMirror = ReflectHelper.getCSRFieldMethodMirror("RW", msb, lsb)
      methodMirror.apply(msb, lsb).asInstanceOf[CSREnumType]
    }

    def apply(msb: Int, lsb: Int) : CSREnumType = this.helper(msb, lsb)

    def apply(bit: Int): CSREnumType = this.helper(bit, bit)
  }

  object CSRWLRLField {
    private def helper(msb: Int, lsb: Int) : CSREnumType = {
      val methodMirror: ru.MethodMirror = ReflectHelper.getCSRFieldMethodMirror("WLRL", msb, lsb)
      methodMirror.apply(msb, lsb).asInstanceOf[CSREnumType]
    }

    def apply(msb: Int, lsb: Int): CSREnumType = this.helper(msb, lsb)
  }

  object PrivMode extends CSREnum with RWApply {
    val U = Value(0.U)
    val S = Value(1.U)
    val M = Value(3.U)

    override def isLegal(enumeration: CSREnumType): Bool = enumeration.isOneOf(U, S, M)
  }

  object VirtMode extends CSREnum with RWApply {
    val Off = Value(0.U)
    val On  = Value(1.U)
  }

  object DebugverMode extends CSREnum with DebugverModeDef with ROApply

  trait DebugverModeDef {
    this: CSREnum =>
    val None = Value(0.U)
    val Spec = Value(4.U)
    val Custom = Value(15.U)
  }
}

object CSRField55Bits extends CSREnum with CSRMacroApply


package xiangshan.backend.fu.NewCSR

import freechips.rocketchip.devices.debug.DebugModuleKey
import org.chipsalliance.cde.config.Parameters
import freechips.rocketchip.rocket.CSRs
import chisel3._
import chisel3.util._
import utils.ConsecutiveOnes
import xiangshan.backend.fu.NewCSR.CSRDefines._
import xiangshan.backend.fu.NewCSR.CSRDefines.{
  CSRWARLField => WARL,
  CSRRWField => RW,
  CSRROField => RO,
}
import xiangshan.backend.fu.NewCSR.CSRFunc._
import xiangshan.backend.fu.NewCSR.CSREvents._
import xiangshan.backend.fu.NewCSR.CSRBundles._
import CSRConfig._
import utility.SignExt
import xiangshan.TriggerAction

import scala.collection.immutable.SeqMap

trait DebugLevel { self: NewCSR =>
  val tselect = Module(new CSRModule("Tselect", new TselectBundle(TriggerNum)) {
    when (this.w.wen && this.w.wdata < TriggerNum.U) {
      reg := this.w.wdata
    }.otherwise {
      reg := reg
    }
  })
    .setAddr(CSRs.tselect)

  val tdata1 = Module(new CSRModule("Tdata1") with HasTdataSink {
    regOut := tdataRead.tdata1
  })
    .setAddr(CSRs.tdata1)

  val tdata2 = Module(new CSRModule("Tdata2") with HasTdataSink {
    regOut := tdataRead.tdata2
  })
    .setAddr(CSRs.tdata2)

  val tdata1RegVec: Seq[CSRModule[_]] = Range(0, TriggerNum).map(i =>
    Module(new CSRModule(s"Trigger$i" + s"_Tdata1", new Tdata1Bundle) with HasTriggerBundle {
      when(wen){
        reg := wdata.writeTdata1(canWriteDmode, chainable).asUInt
      }
    })
  )
  val tdata2RegVec: Seq[CSRModule[_]] = Range(0, TriggerNum).map(i =>
    Module(new CSRModule(s"Trigger$i" + s"_Tdata2", new Tdata2Bundle))
  )

  val tinfo = Module(new CSRModule("Tinfo", new TinfoBundle))
    .setAddr(CSRs.tinfo)

  val dcsr = Module(new CSRModule("Dcsr", new DcsrBundle) with TrapEntryDEventSinkBundle with DretEventSinkBundle with HasNmipBundle {
    when(nmip){
      reg.NMIP := nmip
    }
  })
    .setAddr(CSRs.dcsr)

  val dpc = Module(new CSRModule("Dpc", new Epc) with TrapEntryDEventSinkBundle)
    .setAddr(CSRs.dpc)

  val dscratch0 = Module(new CSRModule("Dscratch0", new DscratchBundle))
    .setAddr(CSRs.dscratch0)

  val dscratch1 = Module(new CSRModule("Dscratch1", new DscratchBundle))
    .setAddr(CSRs.dscratch1)

  val debugCSRMods = Seq(
    tdata1,
    tdata2,
    tselect,
    tinfo,
    dcsr,
    dpc,
    dscratch0,
    dscratch1,
  )

  val debugCSRMap: SeqMap[Int, (CSRAddrWriteBundle[_ <: CSRBundle], UInt)] = SeqMap.from(
    debugCSRMods.map(csr => csr.addr -> (csr.w -> csr.rdata)).iterator
  )

  val debugCSROutMap: SeqMap[Int, UInt] = SeqMap.from(
    debugCSRMods.map(csr => csr.addr -> csr.regOut.asInstanceOf[CSRBundle].asUInt).iterator
  )

  private val tdata1Rdata = Mux1H(
    tdata1RegVec.zipWithIndex.map{case (mod, idx) => (tselect.rdata === idx.U) -> mod.rdata}
  )

  private val tdata2Rdata = Mux1H(
    tdata2RegVec.zipWithIndex.map{case (mod, idx) => (tselect.rdata === idx.U) -> mod.rdata}
  )

  debugCSRMods.foreach { mod =>
    mod match {
      case m: HasTdataSink =>
        m.tdataRead.tdata1 := tdata1Rdata
        m.tdataRead.tdata2 := tdata2Rdata
      case _ =>
    }
  }

}

// tselect
class TselectBundle(triggerNum: Int) extends CSRBundle{
  override val len: Int = log2Up(triggerNum)
  val ALL = WARL(len - 1, 0, wNoEffectWhen(WriteTselect)).withReset(0.U)
  def WriteTselect(wdata: UInt) = {
    wdata >= triggerNum.U
  }
}

// tdata1
class Tdata1Bundle extends CSRBundle{
  val TYPE    = Tdata1Type(63, 60, wNoFilter).withReset(Tdata1Type.Disabled)
  val DMODE   = RW(59).withReset(0.U)
  val DATA    = RW(58, 0).withReset(0.U)

  def getTriggerAction: CSREnumType = {
    val res = Wire(new Mcontrol6)
    res := this.asUInt
    res.ACTION
  }

  def writeTdata1(canWriteDmode: Bool, chainable: Bool): Tdata1Bundle = {
    val res = Wire(new Tdata1Bundle)
    res := this.asUInt
    val dmode = this.DMODE.asBool && canWriteDmode
    res.TYPE := this.TYPE.legalize.asUInt
    res.DMODE := dmode
    when(this.TYPE.isLegal) {
      val mcontrol6Res = Wire(new Mcontrol6)
      mcontrol6Res := this.DATA.asUInt
      res.DATA := mcontrol6Res.writeData(dmode, chainable).asUInt
    }.otherwise{
      res.DATA := 0.U
    }
   res
  }
}

class Mcontrol6 extends CSRBundle{
  override val len: Int = 59
  // xiangshan don't support match = NAPOT
  val UNCERTAIN   = RO(26).withReset(0.U)
  val HIT1        = RO(25).withReset(0.U)
  val VS          = RW(24).withReset(0.U)
  val VU          = RW(23).withReset(0.U)
  val HIT0        = RO(22).withReset(0.U)
  val SELECT      = RO(21).withReset(0.U)
  val SIZE        = RO(18, 16).withReset(0.U)
  val ACTION      = TrigAction(15, 12, wNoFilter).withReset(TrigAction.BreakpointExp)
  val CHAIN       = RW(11).withReset(0.U)
  val MATCH       = TrigMatch(10, 7, wNoFilter).withReset(TrigMatch.EQ)
  val M           = RW(6).withReset(0.U)
  val UNCERTAINEN = RO(5).withReset(0.U)
  val S           = RW(4).withReset(0.U)
  val U           = RW(3).withReset(0.U)
  val EXECUTE     = RW(2).withReset(0.U)
  val STORE       = RW(1).withReset(0.U)
  val LOAD        = RW(0).withReset(0.U)

  def writeData(dmode: Bool, chainable: Bool): Mcontrol6 = {
    val res = Wire(new Mcontrol6)
    res := this.asUInt
    res.UNCERTAIN   := 0.U
    res.HIT1        := 0.U
    res.HIT0        := 0.U
    res.SELECT      := 0.U
    res.SIZE        := 0.U
    res.ACTION      := this.ACTION.legalize(dmode).asUInt
    res.CHAIN       := this.CHAIN.asBool && chainable
    res.MATCH       := this.MATCH.legalize.asUInt
    res.UNCERTAINEN := 0.U
    res
  }
  def isFetchTrigger: Bool = this.EXECUTE.asBool
  def isMemAccTrigger: Bool = this.STORE || this.LOAD
}

object Tdata1Type extends CSREnum with WARLApply {
  val None         = Value(0.U)
  val Legacy       = Value(1.U)
  val Mcontrol     = Value(2.U)
  val Icount       = Value(3.U)
  val Itrigger     = Value(4.U)
  val Etrigger     = Value(5.U)
  val Mcontrol6    = Value(6.U)
  val Tmexttrigger = Value(7.U)
  val Disabled     = Value(15.U)

  override def isLegal(enumeration: CSREnumType): Bool = enumeration.isOneOf(Mcontrol6)

  override def legalize(enumeration: CSREnumType): CSREnumType = {
    val res = WireInit(enumeration)
    when(!enumeration.isLegal){
      res := Disabled.asUInt
    }
    res
  }
}

object TrigAction extends CSREnum with WARLApply {
  val BreakpointExp = Value(0.U) // raise breakpoint exception
  val DebugMode     = Value(1.U) // enter debug mode
  val TraceOn       = Value(2.U)
  val TraceOff      = Value(3.U)
  val TraceNotify   = Value(4.U)

  override def isLegal(enumeration: CSREnumType, dmode: Bool): Bool = enumeration.isOneOf(BreakpointExp) || enumeration.isOneOf(DebugMode) && dmode

  override def legalize(enumeration: CSREnumType, dmode: Bool): CSREnumType = {
    val res = WireInit(enumeration)
    when(!enumeration.isLegal(dmode)){
      res := BreakpointExp
    }
    res.asInstanceOf[CSREnumType]
  }
}

object TrigMatch extends CSREnum with WARLApply {
  val EQ        = Value(0.U)
  val NAPOT     = Value(1.U)
  val GE        = Value(2.U)
  val LT        = Value(3.U)
  val MASK_LO   = Value(4.U)
  val MASK_HI   = Value(5.U)
  val NE        = Value(8.U)  // not eq
  val NNAPOT    = Value(9.U)  // not napot
  val NMASK_LO  = Value(12.U) // not mask low
  val NMASK_HI  = Value(13.U) // not mask high
  def isRVSpecLegal(enumeration: CSREnumType) : Bool = enumeration.isOneOf(
    EQ, NAPOT, GE, LT, MASK_LO, MASK_HI,
    NE, NNAPOT, NMASK_LO, NMASK_HI,
  )
  override def isLegal(enumeration: CSREnumType): Bool = enumeration.isOneOf(EQ, GE, LT)

  override def legalize(enumeration: CSREnumType): CSREnumType = {
    val res = WireInit(enumeration)
    when(!enumeration.isLegal){
      res := EQ
    }
    res.asInstanceOf[CSREnumType]
  }
}

// tdata2
class Tdata2Bundle extends OneFieldBundle

// Tinfo
class TinfoBundle extends CSRBundle{
  // Version isn't in version 0.13
  val VERSION     = RO(31, 24).withReset(0.U)
  // only support mcontrol6
  val MCONTROL6EN = RO(6).withReset(1.U)
}

// Dscratch
class DscratchBundle extends OneFieldBundle

class DcsrBundle extends CSRBundle {
  override val len: Int = 32
  val DEBUGVER  = DcsrDebugVer(31, 28).withReset(DcsrDebugVer.Spec) // Debug implementation as it described in 0.13 draft
  val EXTCAUSE  =           RO(26, 24).withReset(0.U)
  val CETRIG    =           RW(    19).withReset(0.U)
  // All ebreak Privileges are RW, instead of WARL, since XiangShan support U/S/VU/VS.
  val EBREAKVS  =           RW(    17).withReset(0.U)
  val EBREAKVU  =           RW(    16).withReset(0.U)
  val EBREAKM   =           RW(    15).withReset(0.U)
  val EBREAKS   =           RW(    13).withReset(0.U)
  val EBREAKU   =           RW(    12).withReset(0.U)
  // STEPIE is RW, instead of WARL, since XiangShan support interrupts being enabled single stepping.
  val STEPIE    =           RW(    11).withReset(0.U)
  val STOPCOUNT =           RW(    10).withReset(0.U)
  val STOPTIME  =           RW(     9).withReset(0.U)
  val CAUSE     =    DcsrCause( 8,  6).withReset(DcsrCause.None)
  val V         =     VirtMode(     5).withReset(VirtMode.Off)
  // MPRVEN is RW, instead of WARL, since XiangShan support use mstatus.mprv in debug mode
  // Whether use mstatus.mprv
  val MPRVEN    =           RW(     4).withReset(0.U)
  val NMIP      =           RO(     3).withReset(0.U)
  // MPRVEN is RW, instead of WARL, since XiangShan support use mstatus.mprv in debug mode
  val STEP      =           RW(     2).withReset(0.U)
  val PRV       =     PrivMode( 1,  0).withReset(PrivMode.M)
}

object DcsrDebugVer extends CSREnum with ROApply {
  val None    = Value(0.U)
  val Spec    = Value(4.U)
  val Custom  = Value(15.U)
}

object DcsrCause extends CSREnum with ROApply {
  val None         = Value(0.U)
  val Ebreak       = Value(1.U)
  val Trigger      = Value(2.U)
  val Haltreq      = Value(3.U)
  val Step         = Value(4.U)
  val Resethaltreq = Value(5.U)
  val Group        = Value(6.U)
  val Other        = Value(7.U)
}

trait HasTdataSink { self: CSRModule[_] =>
  val tdataRead = IO(Input(new Bundle {
    val tdata1 = UInt(XLEN.W)
    val tdata2 = UInt(XLEN.W)
  }))
}
trait HasTriggerBundle { self: CSRModule[_] =>
  val canWriteDmode = IO(Input(Bool()))
  val chainable = IO(Input(Bool()))
}

trait HasNmipBundle { self: CSRModule[_] =>
  val nmip = IO(Input(Bool()))
}

/**
 * debug Module MMIO Addr
 */
trait DebugMMIO {
  implicit val p: Parameters

  def debugMMIO = p(DebugModuleKey).get

  def BASE = debugMMIO.baseAddress
  def DebugEntry     = BASE + 0x800
  def DebugException = BASE + 0x808
  def HALTED         = BASE + 0x100

  def RESUMING       = BASE + 0x108
  def EXCEPTION      = BASE + 0x10C
  def WHERETO        = BASE + 0x300
  def DATA           = BASE + 0x380
  def IMPEBREAK      = DATA - 0x4
  def PROGBUF        = DATA - 4 * debugMMIO.nProgramBufferWords
  def ABSTRACT       = PROGBUF - 4 * (if(debugMMIO.atzero) 2 else 5)
  def FLAGS          = BASE + 0x400
}

object TriggerUtil {
  /**
   * Check if chain vector is legal
   * @param chainVec
   * @param chainLen
   * @return true.B if the max length of chain don't exceed the permitted length
   */
  def TriggerCheckChainLegal(chainVec: Seq[Bool], chainLen: Int): Bool = {
    !ConsecutiveOnes(chainVec, chainLen)
  }

  /**
   * Generate Trigger action
   * @return triggerAction return
   * @param  triggerCanFireVec
   * @param  actionVec tdata.action
   * @param  triggerCanRaiseBpExp from csr
   */
  def triggerActionGen(triggerAction: UInt, triggerCanFireVec: Vec[Bool], actionVec: Vec[UInt], triggerCanRaiseBpExp: Bool): Unit = {
    // More than one triggers can hit at the same time, but only fire one.
    // We select the first hit trigger to fire.
    val hasTriggerFire    = triggerCanFireVec.asUInt.orR
    val triggerFireOH     = PriorityEncoderOH(triggerCanFireVec)
    val triggerFireAction = PriorityMux(triggerFireOH, actionVec).asUInt
    val actionIsBPExp     = hasTriggerFire && (triggerFireAction === TrigAction.BreakpointExp.asUInt)
    val actionIsDmode     = hasTriggerFire && (triggerFireAction === TrigAction.DebugMode.asUInt)
    val breakPointExp     = actionIsBPExp && triggerCanRaiseBpExp

    // todo: add more for trace
    triggerAction := MuxCase(TriggerAction.None, Seq(
      breakPointExp -> TriggerAction.BreakpointExp,
      actionIsDmode -> TriggerAction.DebugMode,
    ))
  }
}