/*
inst and data input order:
	first a CSR inst for setting VPU CSR environment,
	then two load insts with incresing data sequence for inputting data,
	then sixteen arithmetic or logic insts for data changing,
        final a store inst for data output;
print insts' name, insts' fields and data for better understanding waveform.
*/

package vpu

import chisel3.iotesters._
import chisel3.util._
import java.io.File
import java.io.PrintWriter

class VPUTest(c: VPU_64V64E1L) extends PeekPokeTester(c) {
  val writer = new PrintWriter(new File("/home/liangzh/Desktop/ChiselProjects/Vector/src/test/scala/VPU/VPUResult"))

/////////////////////////////////environment setting////////////////////////////
  //set CSR and initial value for vector regfile
  val vsew       = rnd.nextInt(log2Ceil(c.VLEN / 8))
  val vlmul      = rnd.nextInt(log2Ceil(c.LMULMAX) + 1)
  val vtype      = BigInt(vsew << 2 | vlmul)
  val SEW        = 8 << vsew
  val LMUL       = 1 << vlmul
  val VLMAX      = LMUL * c.VLEN / SEW
  val MLEN       = SEW / LMUL
  val AVL        = rnd.nextInt(2*VLMAX+1)
  val vl = if(rs1 == "00000") VLMAX
           else if(AVL <= VLMAX) AVL
           else if(AVL < 2*VLMAX) AVL/2 + 1
           else VLMAX
  val atomVal    = rnd.nextInt(1 << 8)
  val atomValStr = "0"*(2-atomVal.toHexString.length) + atomVal.toHexString
  val elemValStr = ("0"*(2-atomVal.toHexString.length) + atomVal.toHexString)*(1 << vsew)
  val elemVal    = BigInt(elemValStr, 16)

  //print environment setting
  writer.println("VLEN  -- the number of bits in a vector register       = " + c.VLEN)
  writer.println("XLEN  -- the number of bits in a Rocket GPR            = " + c.XLEN)
  writer.println()
  writer.println("VLMAX -- maximum number of elements in a calculation   = " + VLMAX)
  writer.println("AVL   -- application vector length                     = " + AVL)
  writer.println("vl    -- actual vector length                          = " + vl)
  writer.println()
  writer.println("vsew  -- dynamic standard width(SEW) encoding          = " + vsew)
  writer.println("SEW   -- dynamic standard width                        = " + SEW)
  writer.println("vlmul -- vector register grouping encoding             = " + vlmul)
  writer.println("LMUL  -- vector register grouping                      = " + LMUL)
  writer.println()
  writer.println("MLEN  -- the size of each mask element in bits         = " + MLEN)
  writer.println()
  writer.println("atomic value(Hex) -- to construct load input values    = " + atomValStr)
  writer.println("element value(Hex) -- the first load input value       = " + elemValStr)
  writer.println()
  writer.println()
/////////////////////////////////environment setting////////////////////////////

  def VTYPEI: String = "0"*(11-vtype.toString(2).length)+vtype.toString(2)
  def vm: String = BigInt(1, scala.util.Random).toString(2)
  def VlmulRF:  String = { val rf = (BigInt(5, scala.util.Random) & (32 - LMUL)).toString(2); "0"*(5-rf.length)+rf }
  def SingleRF: String = { val rf = BigInt(5, scala.util.Random).toString(2); "0"*(5-rf.length)+rf }
  def VmlenRF(start: BigInt): String = { val rf = (BigInt(vlmul, scala.util.Random) + start).toString(2); "0"*(5-rf.length)+rf }

  val vtypei = VTYPEI
  val ldDstField = Array(VlmulRF, VlmulRF)
  val ldForVs1 = ldDstField(0)
  val ldForVs2 = ldDstField(1)
  val (rs1, rs2) = (SingleRF, SingleRF)
  val (vs1m, vs2m) = (VmlenRF(BigInt(ldDstField(0), 2)), VmlenRF(BigInt(ldDstField(1), 2)))
  val (vs1s, vs2s) = (VmlenRF(BigInt(ldDstField(0), 2)), VmlenRF(BigInt(ldDstField(1), 2)))
  val (rd, vde, vdm, vds) = (SingleRF, VlmulRF, SingleRF, SingleRF)
  val (vs2e, vs3e) = (VlmulRF, VlmulRF)
  val imm = SingleRF

///////////////////////////instructions name-code pairs/////////////////////////
  //AL Insts
  def VADD_VV     (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("000000"+vm +vs2e   +vs1e   +"000"+vde+"1010111", "VADD_VV     ")
  def VADD_VX     (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("000000"+vm +vs2e   +rs1    +"100"+vde+"1010111", "VADD_VX     ")
  def VADD_VI     (vde: String = vde, vs2e: String = ldForVs2, imm:  String = imm,      vm: String = vm) = ("000000"+vm +vs2e   +imm    +"011"+vde+"1010111", "VADD_VI     ")
  def VSUB_VV     (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("000010"+vm +vs2e   +vs1e   +"000"+vde+"1010111", "VSUB_VV     ")
  def VSUB_VX     (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("000010"+vm +vs2e   +rs1    +"100"+vde+"1010111", "VSUB_VX     ")
  def VRSUB_VX    (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("000011"+vm +vs2e   +rs1    +"100"+vde+"1010111", "VRSUB_VX    ")
  def VRSUB_VI    (vde: String = vde, vs2e: String = ldForVs2, imm:  String = imm,      vm: String = vm) = ("000011"+vm +vs2e   +imm    +"011"+vde+"1010111", "VRSUB_VI    ")
  val addInsts = Array(VADD_VV(), VADD_VX(), VADD_VI(),
                       VSUB_VV(), VSUB_VX(),
                       VRSUB_VX(), VRSUB_VI())

  def VWADDU_VV   (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("110000"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VWADDU_VV   ")
  def VWADDU_VX   (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("110000"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VWADDU_VX   ")
  def VWADD_VV    (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("110001"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VWADD_VV    ")
  def VWADD_VX    (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("110001"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VWADD_VX    ")
  def VWSUBU_VV   (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("110010"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VWSUBU_VV   ")
  def VWSUBU_VX   (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("110010"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VWSUBU_VX   ")
  def VWSUB_VV    (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("110011"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VWSUB_VV    ")
  def VWSUB_VX    (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("110011"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VWSUB_VX    ")
  def VWADDU_WV   (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("110100"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VWADDU_WV   ")
  def VWADDU_WX   (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("110100"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VWADDU_WX   ")
  def VWADD_WV    (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("110101"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VWADD_WV    ")
  def VWADD_WX    (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("110101"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VWADD_WX    ")
  def VWSUBU_WV   (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("110110"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VWSUBU_WV   ")
  def VWSUBU_WX   (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("110110"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VWSUBU_WX   ")
  def VWSUB_WV    (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("110111"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VWSUB_WV    ")
  def VWSUB_WX    (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("110111"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VWSUB_WX    ")
  val waddInsts = Array(VWADDU_VV(), VWADDU_VX(), VWADD_VV(), VWADD_VX(),
                        VWSUBU_VV(), VWSUBU_VX(), VWSUB_VV(), VWSUB_VX(),
                        VWADDU_WV(), VWADDU_WX(), VWADD_WV(), VWADD_WX(),
                        VWSUBU_WV(), VWSUBU_WX(), VWSUB_WV(), VWSUB_WX())

  def VADC_VVM    (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1)                  = ("010000"+"1"+vs2e   +vs1e   +"000"+vde+"1010111", "VADC_VVM    ")
  def VMADC_VVM   (vdm: String = vdm, vs2e: String = ldForVs2, vs1e: String = ldForVs1)                  = ("010001"+"1"+vs2e   +vs1e   +"000"+vdm+"1010111", "VMADC_VVM   ")
  def VADC_VXM    (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1)                       = ("010000"+"1"+vs2e   +rs1    +"100"+vde+"1010111", "VADC_VXM    ")
  def VMADC_VXM   (vdm: String = vdm, vs2e: String = ldForVs2, rs1:  String = rs1)                       = ("010001"+"1"+vs2e   +rs1    +"100"+vdm+"1010111", "VMADC_VXM   ")
  def VADC_VIM    (vde: String = vde, vs2e: String = ldForVs2, imm:  String = imm)                       = ("010000"+"1"+vs2e   +imm    +"011"+vde+"1010111", "VADC_VIM    ")
  def VMADC_VIM   (vdm: String = vdm, vs2e: String = ldForVs2, imm:  String = imm)                       = ("010001"+"1"+vs2e   +imm    +"011"+vdm+"1010111", "VMADC_VIM   ")
  def VSBC_VVM    (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1)                  = ("010010"+"1"+vs2e   +vs1e   +"000"+vde+"1010111", "VSBC_VVM    ")
  def VMSBC_VVM   (vdm: String = vdm, vs2e: String = ldForVs2, vs1e: String = ldForVs1)                  = ("010011"+"1"+vs2e   +vs1e   +"000"+vdm+"1010111", "VMSBC_VVM   ")
  def VSBC_VXM    (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1)                       = ("010010"+"1"+vs2e   +rs1    +"100"+vde+"1010111", "VSBC_VXM    ")
  def VMSBC_VXM   (vdm: String = vdm, vs2e: String = ldForVs2, rs1:  String = rs1)                       = ("010011"+"1"+vs2e   +rs1    +"100"+vdm+"1010111", "VMSBC_VXM   ")
  val adcInsts = Array(VADC_VVM(), VMADC_VVM(), VADC_VXM(), VMADC_VXM(), VADC_VIM(), VMADC_VIM(),
                       VSBC_VVM(), VMSBC_VVM(), VSBC_VXM(), VMSBC_VXM())

  def VAND_VV     (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("001001"+vm +vs2e   +vs1e   +"000"+vde+"1010111", "VAND_VV     ")
  def VAND_VX     (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("001001"+vm +vs2e   +rs1    +"100"+vde+"1010111", "VAND_VX     ")
  def VAND_VI     (vde: String = vde, vs2e: String = ldForVs2, imm:  String = imm,      vm: String = vm) = ("001001"+vm +vs2e   +imm    +"011"+vde+"1010111", "VAND_VI     ")
  def VOR_VV      (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("001010"+vm +vs2e   +vs1e   +"000"+vde+"1010111", "VOR_VV      ")
  def VOR_VX      (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("001010"+vm +vs2e   +rs1    +"100"+vde+"1010111", "VOR_VX      ")
  def VOR_VI      (vde: String = vde, vs2e: String = ldForVs2, imm:  String = imm,      vm: String = vm) = ("001010"+vm +vs2e   +imm    +"011"+vde+"1010111", "VOR_VI      ")
  def VXOR_VV     (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("001011"+vm +vs2e   +vs1e   +"000"+vde+"1010111", "VXOR_VV     ")
  def VXOR_VX     (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("001011"+vm +vs2e   +rs1    +"100"+vde+"1010111", "VXOR_VX     ")
  def VXOR_VI     (vde: String = vde, vs2e: String = ldForVs2, imm:  String = imm,      vm: String = vm) = ("001011"+vm +vs2e   +imm    +"011"+vde+"1010111", "VXOR_VI     ")
  val bitInsts = Array(VAND_VV(), VAND_VX(), VAND_VI(),
                       VOR_VV(), VOR_VX(), VOR_VI(),
                       VXOR_VV(), VXOR_VX(), VXOR_VI())

  def VSLL_VV     (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("100101"+vm +vs2e   +vs1e   +"000"+vde+"1010111", "VSLL_VV     ")
  def VSLL_VX     (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("100101"+vm +vs2e   +rs1    +"100"+vde+"1010111", "VSLL_VX     ")
  def VSLL_VI     (vde: String = vde, vs2e: String = ldForVs2, imm:  String = imm,      vm: String = vm) = ("100101"+vm +vs2e   +imm    +"011"+vde+"1010111", "VSLL_VI     ")
  def VSRL_VV     (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("101000"+vm +vs2e   +vs1e   +"000"+vde+"1010111", "VSRL_VV     ")
  def VSRL_VX     (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("101000"+vm +vs2e   +rs1    +"100"+vde+"1010111", "VSRL_VX     ")
  def VSRL_VI     (vde: String = vde, vs2e: String = ldForVs2, imm:  String = imm,      vm: String = vm) = ("101000"+vm +vs2e   +imm    +"011"+vde+"1010111", "VSRL_VI     ")
  def VSRA_VV     (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("101001"+vm +vs2e   +vs1e   +"000"+vde+"1010111", "VSRA_VV     ")
  def VSRA_VX     (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("101001"+vm +vs2e   +rs1    +"100"+vde+"1010111", "VSRA_VX     ")
  def VSRA_VI     (vde: String = vde, vs2e: String = ldForVs2, imm:  String = imm,      vm: String = vm) = ("101001"+vm +vs2e   +imm    +"011"+vde+"1010111", "VSRA_VI     ")
  val shiftInsts = Array(VSLL_VV(), VSLL_VX(), VSLL_VI(),
                         VSRL_VV(), VSRL_VX(), VSRL_VI(),
                         VSRA_VV(), VSRA_VX(), VSRA_VI())

  def VNSRL_VV    (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("101100"+vm +vs2e   +vs1e   +"000"+vde+"1010111", "VNSRL_VV    ")
  def VNSRL_VX    (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("101100"+vm +vs2e   +rs1    +"100"+vde+"1010111", "VNSRL_VX    ")
  def VNSRL_VI    (vde: String = vde, vs2e: String = ldForVs2, imm:  String = imm,      vm: String = vm) = ("101100"+vm +vs2e   +imm    +"011"+vde+"1010111", "VNSRL_VI    ")
  def VNSRA_VV    (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("101101"+vm +vs2e   +vs1e   +"000"+vde+"1010111", "VNSRA_VV    ")
  def VNSRA_VX    (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("101101"+vm +vs2e   +rs1    +"100"+vde+"1010111", "VNSRA_VX    ")
  def VNSRA_VI    (vde: String = vde, vs2e: String = ldForVs2, imm:  String = imm,      vm: String = vm) = ("101101"+vm +vs2e   +imm    +"011"+vde+"1010111", "VNSRA_VI    ")
  val nshiftInsts = Array(VNSRL_VV(), VNSRL_VX(), VNSRL_VI(),
                          VNSRA_VV(), VNSRA_VX(), VNSRA_VI())

  def VMSEQ_VV    (vdm: String = vdm, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("011000"+vm +vs2e   +vs1e   +"000"+vdm+"1010111", "VMSEQ_VV    ")
  def VMSEQ_VX    (vdm: String = vdm, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("011000"+vm +vs2e   +rs1    +"100"+vdm+"1010111", "VMSEQ_VX    ")
  def VMSEQ_VI    (vdm: String = vdm, vs2e: String = ldForVs2, imm:  String = imm,      vm: String = vm) = ("011000"+vm +vs2e   +imm    +"011"+vdm+"1010111", "VMSEQ_VI    ")
  def VMSNE_VV    (vdm: String = vdm, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("011001"+vm +vs2e   +vs1e   +"000"+vdm+"1010111", "VMSNE_VV    ")
  def VMSNE_VX    (vdm: String = vdm, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("011001"+vm +vs2e   +rs1    +"100"+vdm+"1010111", "VMSNE_VX    ")
  def VMSNE_VI    (vdm: String = vdm, vs2e: String = ldForVs2, imm:  String = imm,      vm: String = vm) = ("011001"+vm +vs2e   +imm    +"011"+vdm+"1010111", "VMSNE_VI    ")
  def VMSLTU_VV   (vdm: String = vdm, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("011010"+vm +vs2e   +vs1e   +"000"+vdm+"1010111", "VMSLTU_VV   ")
  def VMSLTU_VX   (vdm: String = vdm, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("011010"+vm +vs2e   +rs1    +"100"+vdm+"1010111", "VMSLTU_VX   ")
  def VMSLT_VV    (vdm: String = vdm, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("011011"+vm +vs2e   +vs1e   +"100"+vdm+"1010111", "VMSLT_VV    ")
  def VMSLT_VX    (vdm: String = vdm, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("011011"+vm +vs2e   +rs1    +"100"+vdm+"1010111", "VMSLT_VX    ")
  def VMSLEU_VV   (vdm: String = vdm, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("011100"+vm +vs2e   +vs1e   +"000"+vdm+"1010111", "VMSLEU_VV   ")
  def VMSLEU_VX   (vdm: String = vdm, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("011100"+vm +vs2e   +rs1    +"100"+vdm+"1010111", "VMSLEU_VX   ")
  def VMSLEU_VI   (vdm: String = vdm, vs2e: String = ldForVs2, imm:  String = imm,      vm: String = vm) = ("011100"+vm +vs2e   +imm    +"011"+vdm+"1010111", "VMSLEU_VI   ")
  def VMSLE_VV    (vdm: String = vdm, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("011101"+vm +vs2e   +vs1e   +"000"+vdm+"1010111", "VMSLE_VV    ")
  def VMSLE_VX    (vdm: String = vdm, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("011101"+vm +vs2e   +rs1    +"100"+vdm+"1010111", "VMSLE_VX    ")
  def VMSLE_VI    (vdm: String = vdm, vs2e: String = ldForVs2, imm:  String = imm,      vm: String = vm) = ("011101"+vm +vs2e   +imm    +"011"+vdm+"1010111", "VMSLE_VI    ")
  def VMSGTU_VX   (vdm: String = vdm, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("011110"+vm +vs2e   +rs1    +"100"+vdm+"1010111", "VMSGTU_VX   ")
  def VMSGTU_VI   (vdm: String = vdm, vs2e: String = ldForVs2, imm:  String = imm,      vm: String = vm) = ("011110"+vm +vs2e   +imm    +"011"+vdm+"1010111", "VMSGTU_VI   ")
  def VMSGT_VX    (vdm: String = vdm, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("011111"+vm +vs2e   +rs1    +"100"+vdm+"1010111", "VMSGT_VX    ")
  def VMSGT_VI    (vdm: String = vdm, vs2e: String = ldForVs2, imm:  String = imm,      vm: String = vm) = ("011111"+vm +vs2e   +imm    +"011"+vdm+"1010111", "VMSGT_VI    ")
  val cmpInsts = Array(VMSEQ_VV(), VMSEQ_VX(), VMSEQ_VI(),
                       VMSNE_VV(), VMSNE_VX(), VMSNE_VI(),
                       VMSLTU_VV(), VMSLTU_VX(),
                       VMSLT_VV(), VMSLT_VX(),
                       VMSLEU_VV(), VMSLEU_VX(), VMSLEU_VI(),
                       VMSLE_VV(), VMSLE_VX(), VMSLE_VI(),
                       VMSGTU_VX(), VMSGTU_VI(),
                       VMSGT_VX(), VMSGT_VI())

  def VMINU_VV    (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("000100"+vm +vs2e   +vs1e   +"000"+vde+"1010111", "VMINU_VV    ")
  def VMINU_VX    (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("000100"+vm +vs2e   +rs1    +"100"+vde+"1010111", "VMINU_VX    ")
  def VMIN_VV     (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("000101"+vm +vs2e   +vs1e   +"000"+vde+"1010111", "VMIN_VV     ")
  def VMIN_VX     (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("000101"+vm +vs2e   +rs1    +"100"+vde+"1010111", "VMIN_VX     ")
  def VMAXU_VV    (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("000110"+vm +vs2e   +vs1e   +"000"+vde+"1010111", "VMAXU_VV    ")
  def VMAXU_VX    (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("000110"+vm +vs2e   +rs1    +"100"+vde+"1010111", "VMAXU_VX    ")
  def VMAX_VV     (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("000111"+vm +vs2e   +vs1e   +"000"+vde+"1010111", "VMAX_VV     ")
  def VMAX_VX     (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("000111"+vm +vs2e   +rs1    +"100"+vde+"1010111", "VMAX_VX     ")
  val minInsts = Array(VMINU_VV(), VMINU_VX(),
                       VMIN_VV(), VMIN_VX(),
                       VMAXU_VV(), VMAXU_VX(),
                       VMAX_VV(), VMAX_VX())

  def VMERGE_VVM  (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("010111"+vm +vs2e   +vs1e   +"000"+vde+"1010111", "VMERGE_VVM  ")
  def VMERGE_VXM  (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("010111"+vm +vs2e   +rs1    +"100"+vde+"1010111", "VMERGE_VXM  ")
  def VMERGE_VIM  (vde: String = vde, vs2e: String = ldForVs2, imm:  String = imm,      vm: String = vm) = ("010111"+vm +vs2e   +imm    +"011"+vde+"1010111", "VMERGE_VIM  ")
  val mergeInsts = Array(VMERGE_VVM(), VMERGE_VXM(), VMERGE_VIM())

  def VPOPC_M     (rd: String = rd,   vs2m: String = vs2m,                              vm: String = vm) = ("010000"+vm +vs2m   +"10000"+"010"+rd +"1010111", "VPOPC_M     ")
  val popcInst = Array(VPOPC_M())

  def VFIRST_M    (rd: String = rd,   vs2m: String = vs2m,                              vm: String = vm) = ("010000"+vm +vs2m   +"10001"+"010"+rd +"1010111", "VFIRST_M    ")
  val firstInst = Array(VFIRST_M())

  def VMSBF_M     (vdm: String = vdm, vs2m: String = vs2m,                              vm: String = vm) = ("010100"+vm +vs2m   +"00001"+"010"+vdm+"1010111", "VMSBF_M     ")
  def VMSOF_M     (vdm: String = vdm, vs2m: String = vs2m,                              vm: String = vm) = ("010100"+vm +vs2m   +"00010"+"010"+vdm+"1010111", "VMSOF_M     ")
  def VMSIF_M     (vdm: String = vdm, vs2m: String = vs2m,                              vm: String = vm) = ("010100"+vm +vs2m   +"00011"+"010"+vdm+"1010111", "VMSIF_M     ")
  val midxInsts = Array(VMSBF_M(), VMSOF_M(), VMSIF_M())

  def VIOTA_M     (vde: String = vde, vs2m: String = vs2m,                              vm: String = vm) = ("010100"+vm +vs2m   +"10000"+"010"+vde+"1010111", "VIOTA_M     ")
  val iotaInst = Array(VIOTA_M())

  def VID_V       (vde: String = vde,                                                   vm: String = vm) = ("010100"+vm +"00000"+"10001"+"010"+vde+"1010111", "VID_V       ")
  val idInst = Array(VID_V())

  def VMANDNOT_MM (vdm: String = vdm, vs2m: String = vs2m,     vs1m: String = vs1m)                      = ("011000"+"1"+vs2m   +vs1m   +"010"+vdm+"1010111", "VMANDNOT_MM ")
  def VMAND_MM    (vdm: String = vdm, vs2m: String = vs2m,     vs1m: String = vs1m)                      = ("011001"+"1"+vs2m   +vs1m   +"010"+vdm+"1010111", "VMAND_MM    ")
  def VMOR_MM     (vdm: String = vdm, vs2m: String = vs2m,     vs1m: String = vs1m)                      = ("011010"+"1"+vs2m   +vs1m   +"010"+vdm+"1010111", "VMOR_MM     ")
  def VMXOR_MM    (vdm: String = vdm, vs2m: String = vs2m,     vs1m: String = vs1m)                      = ("011011"+"1"+vs2m   +vs1m   +"010"+vdm+"1010111", "VMXOR_MM    ")
  def VMORNOT_MM  (vdm: String = vdm, vs2m: String = vs2m,     vs1m: String = vs1m)                      = ("011100"+"1"+vs2m   +vs1m   +"010"+vdm+"1010111", "VMORNOT_MM  ")
  def VMNAND_MM   (vdm: String = vdm, vs2m: String = vs2m,     vs1m: String = vs1m)                      = ("011101"+"1"+vs2m   +vs1m   +"010"+vdm+"1010111", "VMNAND_MM   ")
  def VMNOR_MM    (vdm: String = vdm, vs2m: String = vs2m,     vs1m: String = vs1m)                      = ("011110"+"1"+vs2m   +vs1m   +"010"+vdm+"1010111", "VMNOR_MM    ")
  def VMXNOR_MM   (vdm: String = vdm, vs2m: String = vs2m,     vs1m: String = vs1m)                      = ("011111"+"1"+vs2m   +vs1m   +"010"+vdm+"1010111", "VMXNOR_MM   ")
  val mbitInsts = Array(VMANDNOT_MM(), VMAND_MM(), VMOR_MM(), VMXOR_MM(), VMORNOT_MM(), VMNAND_MM(), VMNOR_MM(), VMXNOR_MM())

  def VMUL_VV     (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("100101"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VMUL_VV     ")
  def VMUL_VX     (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("100101"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VMUL_VX     ")
  def VMULH_VV    (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("100111"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VMULH_VV    ")
  def VMULH_VX    (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("100111"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VMULH_VX    ")
  def VMULHU_VV   (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("100100"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VMULHU_VV   ")
  def VMULHU_VX   (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("100100"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VMULHU_VX   ")
  def VMULHSU_VV  (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("100110"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VMULHSU_VV  ")
  def VMULHSU_VX  (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("100110"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VMULHSU_VX  ")
  val mulInsts = Array(VMUL_VV(), VMUL_VX(),
                       VMULH_VV(), VMULH_VX(),
                       VMULHU_VV(), VMULHU_VX(),
                       VMULHSU_VV(), VMULHSU_VX())

  def VWMUL_VV    (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("111011"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VWMUL_VV    ")
  def VWMUL_VX    (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("111011"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VWMUL_VX    ")
  def VWMULU_VV   (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("111000"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VWMULU_VV   ")
  def VWMULU_VX   (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("111000"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VWMULU_VX   ")
  def VWMULSU_VV  (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("111010"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VWMULSU_VV  ")
  def VWMULSU_VX  (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("111010"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VWMULSU_VX  ")
  val wmulInsts = Array(VWMUL_VV(), VWMUL_VX(),
                        VWMULU_VV(), VWMULU_VX(),
                        VWMULSU_VV(), VWMULSU_VX())

  def VDIV_VV     (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("100001"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VDIV_VV     ")
  def VDIV_VX     (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("100001"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VDIV_VX     ")
  def VDIVU_VV    (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("100000"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VDIVU_VV    ")
  def VDIVU_VX    (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("100000"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VDIVU_VX    ")
  def VREM_VV     (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("100011"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VREM_VV     ")
  def VREM_VX     (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("100011"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VREM_VX     ")
  def VREMU_VV    (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("100010"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VREMU_VV    ")
  def VREMU_VX    (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("100010"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VREMU_VX    ")
  val divInsts = Array(VDIV_VV(), VDIV_VX(), VDIVU_VV(), VDIVU_VX(),
                       VREM_VV(), VREM_VX(), VREMU_VV(), VREMU_VX())

  def VMADD_VV    (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("101001"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VMADD_VV    ")
  def VMADD_VX    (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("101001"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VMADD_VX    ")
  def VNMSUB_VV   (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("101011"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VNMSUB_VV   ")
  def VNMSUB_VX   (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("101011"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VNMSUB_VX   ")
  def VMACC_VV    (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("101101"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VMACC_VV    ")
  def VMACC_VX    (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("101101"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VMACC_VX    ")
  def VNMSAC_VV   (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("101111"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VNMSAC_VV   ")
  def VNMSAC_VX   (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("101111"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VNMSAC_VX   ")
  val maddInsts = Array(VMADD_VV(), VMADD_VX(),
                        VNMSUB_VV(), VNMSUB_VX(),
                        VMACC_VV(), VMACC_VX(),
                        VNMSAC_VV(), VNMSAC_VX())

  def VWMACCU_VV  (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("111100"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VWMACCU_VV  ")
  def VWMACCU_VX  (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("111100"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VWMACCU_VX  ")
  def VWMACC_VV   (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("111101"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VWMACC_VV   ")
  def VWMACC_VX   (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("111101"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VWMACC_VX   ")
  def VWMACCSU_VV (vde: String = vde, vs2e: String = ldForVs2, vs1e: String = ldForVs1, vm: String = vm) = ("111110"+vm +vs2e   +vs1e   +"010"+vde+"1010111", "VWMACCSU_VV ")
  def VWMACCSU_VX (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("111110"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VWMACCSU_VX ")
  def VWMACCUS_VX (vde: String = vde, vs2e: String = ldForVs2, rs1:  String = rs1,      vm: String = vm) = ("111111"+vm +vs2e   +rs1    +"110"+vde+"1010111", "VWMACCUS_VX ")
  val wmaddInsts = Array(VWMACCU_VV(), VWMACCU_VX(),
                         VWMACC_VV(), VWMACC_VX(),
                         VWMACCSU_VV(), VWMACCSU_VX(),
                         VWMACCUS_VX())

  def VWREDSUMU_VS(vds: String = vds, vs2e: String = ldForVs2, vs1s: String = vs1s,     vm: String = vm) = ("110000"+vm +vs2e   +vs1s   +"000"+vds+"1010111", "VWREDSUMU_VS")
  def VWREDSUM_VS (vds: String = vds, vs2e: String = ldForVs2, vs1s: String = vs1s,     vm: String = vm) = ("110001"+vm +vs2e   +vs1s   +"000"+vds+"1010111", "VWREDSUM_VS ")
  def VREDSUM_VS  (vds: String = vds, vs2e: String = ldForVs2, vs1s: String = vs1s,     vm: String = vm) = ("000000"+vm +vs2e   +vs1s   +"010"+vds+"1010111", "VREDSUM_VS  ")
  def VREDAND_VS  (vds: String = vds, vs2e: String = ldForVs2, vs1s: String = vs1s,     vm: String = vm) = ("000001"+vm +vs2e   +vs1s   +"010"+vds+"1010111", "VREDAND_VS  ")
  def VREDOR_VS   (vds: String = vds, vs2e: String = ldForVs2, vs1s: String = vs1s,     vm: String = vm) = ("000010"+vm +vs2e   +vs1s   +"010"+vds+"1010111", "VREDOR_VS   ")
  def VREDXOR_VS  (vds: String = vds, vs2e: String = ldForVs2, vs1s: String = vs1s,     vm: String = vm) = ("000011"+vm +vs2e   +vs1s   +"010"+vds+"1010111", "VREDXOR_VS  ")
  def VREDMINU_VS (vds: String = vds, vs2e: String = ldForVs2, vs1s: String = vs1s,     vm: String = vm) = ("000100"+vm +vs2e   +vs1s   +"010"+vds+"1010111", "VREDMINU_VS ")
  def VREDMIN_VS  (vds: String = vds, vs2e: String = ldForVs2, vs1s: String = vs1s,     vm: String = vm) = ("000101"+vm +vs2e   +vs1s   +"010"+vds+"1010111", "VREDMIN_VS  ")
  def VREDMAXU_VS (vds: String = vds, vs2e: String = ldForVs2, vs1s: String = vs1s,     vm: String = vm) = ("000110"+vm +vs2e   +vs1s   +"010"+vds+"1010111", "VREDMAXU_VS ")
  def VREDMAX_VS  (vds: String = vds, vs2e: String = ldForVs2, vs1s: String = vs1s,     vm: String = vm) = ("000111"+vm +vs2e   +vs1s   +"010"+vds+"1010111", "VREDMAX_VS  ")
  val redInsts = Array(VWREDSUMU_VS(), VWREDSUM_VS(), VREDSUM_VS(),
                       VREDAND_VS(), VREDOR_VS(), VREDXOR_VS(),
                       VREDMINU_VS(), VREDMIN_VS(),
                       VREDMAXU_VS(), VREDMAX_VS())
  //integer scalar move instructions
  def VMV_X_S     (rd:  String = rd,  vs2s: String = vs2s)                                               = ("010000"+"1"+vs2s   +"00000"+"010"+rd +"1010111", "VMV_X_S     ")
  def VMV_S_X     (vds: String = vds,                          rs1:  String = rs1)                       = ("010000"+"1"+"00000"+rs1    +"110"+vds+"1010111", "VMV_S_X     ")
  val mvInsts = Array(VMV_X_S(), VMV_S_X())

  //CSR Insts
  def VSETVLI(rd: String = rd, rs1: String = rs1, vtypei: String = vtypei)    = ("0"+vtypei          +rs1    +"111"+rd +"1010111", "VSETVLI    ")
  def VSETVL (rd: String = rd, rs1: String = rs1, rs2: String = rs2)          = ("1"+"000000"+rs2    +rs1    +"111"+rd +"1010111", "VSETVL     ")
  val CSRInsts = Array(VSETVLI(), VSETVL())

  //Load Insts
  def VLB_V  (vde: String,  rs1: String = rs1,                      vm: String = "1") = ("000"+"100"+vm+"00000"+rs1+"000"+vde+"0000111", "VLB_V      ")
  def VLBU_V (vde: String,  rs1: String = rs1,                      vm: String = "1") = ("000"+"000"+vm+"00000"+rs1+"000"+vde+"0000111", "VLBU_V     ")
  def VLSB_V (vde: String,  rs1: String = rs1, rs2:  String = rs2,  vm: String = "1") = ("000"+"110"+vm+rs2    +rs1+"000"+vde+"0000111", "VLSB_V     ")
  def VLSBU_V(vde: String,  rs1: String = rs1, rs2:  String = rs2,  vm: String = "1") = ("000"+"010"+vm+rs2    +rs1+"000"+vde+"0000111", "VLSBU_V    ")
  def VLXB_V (vde: String,  rs1: String = rs1, vs2e: String = vs2e, vm: String = "1") = ("000"+"111"+vm+vs2e   +rs1+"000"+vde+"0000111", "VLXB_V     ")
  def VLXBU_V(vde: String,  rs1: String = rs1, vs2e: String = vs2e, vm: String = "1") = ("000"+"011"+vm+vs2e   +rs1+"000"+vde+"0000111", "VLXBU_V    ")
  def LBInsts(vde: String) = Array(VLB_V(vde), VLBU_V(vde),
                                   VLSB_V(vde), VLSBU_V(vde),
                                   VLXB_V(vde), VLXBU_V(vde))

  def VLH_V  (vde: String,  rs1: String = rs1,                      vm: String = "1") = ("000"+"100"+vm+"00000"+rs1+"101"+vde+"0000111", "VLH_V      ")
  def VLHU_V (vde: String,  rs1: String = rs1,                      vm: String = "1") = ("000"+"000"+vm+"00000"+rs1+"101"+vde+"0000111", "VLHU_V     ")
  def VLSH_V (vde: String,  rs1: String = rs1, rs2:  String = rs2,  vm: String = "1") = ("000"+"110"+vm+rs2    +rs1+"101"+vde+"0000111", "VLSH_V     ")
  def VLSHU_V(vde: String,  rs1: String = rs1, rs2:  String = rs2,  vm: String = "1") = ("000"+"010"+vm+rs2    +rs1+"101"+vde+"0000111", "VLSHU_V    ")

  def VLXHU_V(vde: String,  rs1: String = rs1, vs2e: String = vs2e, vm: String = "1") = ("000"+"011"+vm+vs2e   +rs1+"101"+vde+"0000111", "VLXHU_V    ")
  def LHInsts(vde: String) = Array(VLH_V(vde), VLHU_V(vde),
                                   VLSH_V(vde), VLSHU_V(vde),
                                   VLXH_V(vde), VLXHU_V(vde))

  def VLW_V  (vde: String,  rs1: String = rs1,                      vm: String = "1") = ("000"+"100"+vm+"00000"+rs1+"110"+vde+"0000111", "VLW_V      ")
  def VLWU_V (vde: String,  rs1: String = rs1,                      vm: String = "1") = ("000"+"000"+vm+"00000"+rs1+"110"+vde+"0000111", "VLWU_V     ")
  def VLSW_V (vde: String,  rs1: String = rs1, rs2:  String = rs2,  vm: String = "1") = ("000"+"110"+vm+rs2    +rs1+"110"+vde+"0000111", "VLSW_V     ")
  def VLSWU_V(vde: String,  rs1: String = rs1, rs2:  String = rs2,  vm: String = "1") = ("000"+"010"+vm+rs2    +rs1+"110"+vde+"0000111", "VLSWU_V    ")
  def VLXW_V (vde: String,  rs1: String = rs1, vs2e: String = vs2e, vm: String = "1") = ("000"+"111"+vm+vs2e   +rs1+"110"+vde+"0000111", "VLXW_V     ")
  def VLXWU_V(vde: String,  rs1: String = rs1, vs2e: String = vs2e, vm: String = "1") = ("000"+"011"+vm+vs2e   +rs1+"110"+vde+"0000111", "VLXWU_V    ")
  def LWInsts(vde: String) = Array(VLW_V(vde), VLWU_V(vde),
                                   VLSW_V(vde), VLSWU_V(vde),
                                   VLXW_V(vde), VLXWU_V(vde))

  def VLE_V  (vde: String,  rs1: String = rs1,                      vm: String = "1") = ("000"+"000"+vm+"00000"+rs1+"111"+vde+"0000111", "VLE_V      ")
  def VLSE_V (vde: String,  rs1: String = rs1, rs2:  String = rs2,  vm: String = "1") = ("000"+"010"+vm+rs2    +rs1+"111"+vde+"0000111", "VLSE_V     ")
  def VLXE_V (vde: String,  rs1: String = rs1, vs2e: String = vs2e, vm: String = "1") = ("000"+"011"+vm+vs2e   +rs1+"111"+vde+"0000111", "VLXE_V     ")
  def LEInsts(vde: String) = Array(VLE_V(vde), VLSE_V(vde), VLXE_V(vde))

  //Store Insts
  def VSB_V  (vs3e: String, rs1: String = rs1,                      vm: String = vm) = ("000"+"000"+vm+"00000"+rs1+"000"+vs3e+"0100111", "VSB_V      ")
  def VSSB_V (vs3e: String, rs1: String = rs1, rs2:  String = rs2,  vm: String = vm) = ("000"+"010"+vm+rs2    +rs1+"000"+vs3e+"0100111", "VSSB_V     ")
  def VSXB_V (vs3e: String, rs1: String = rs1, vs2e: String = vs2e, vm: String = vm) = ("000"+"011"+vm+vs2e   +rs1+"000"+vs3e+"0100111", "VSXB_V     ")
  def SBInsts(vs3e: String) = Array(VSB_V(vs3e), VSSB_V(vs3e), VSXB_V(vs3e))

  def VSH_V  (vs3e: String, rs1: String = rs1,                      vm: String = vm) = ("000"+"000"+vm+"00000"+rs1+"101"+vs3e+"0100111", "VSH_V      ")
  def VSSH_V (vs3e: String, rs1: String = rs1, rs2:  String = rs2,  vm: String = vm) = ("000"+"010"+vm+rs2    +rs1+"101"+vs3e+"0100111", "VSSH_V     ")
  def VSXH_V (vs3e: String, rs1: String = rs1, vs2e: String = vs2e, vm: String = vm) = ("000"+"011"+vm+vs2e   +rs1+"101"+vs3e+"0100111", "VSXH_V     ")
  def SHInsts(vs3e: String) = Array(VSH_V(vs3e), VSSH_V(vs3e), VSXH_V(vs3e))

  def VSW_V  (vs3e: String, rs1: String = rs1,                      vm: String = vm) = ("000"+"000"+vm+"00000"+rs1+"110"+vs3e+"0100111", "VSW_V      ")
  def VSSW_V (vs3e: String, rs1: String = rs1, rs2:  String = rs2,  vm: String = vm) = ("000"+"010"+vm+rs2    +rs1+"110"+vs3e+"0100111", "VSSW_V     ")
  def VSXW_V (vs3e: String, rs1: String = rs1, vs2e: String = vs2e, vm: String = vm) = ("000"+"011"+vm+vs2e   +rs1+"110"+vs3e+"0100111", "VSXW_V     ")
  def SWInsts(vs3e: String) = Array(VSW_V(vs3e), VSSW_V(vs3e), VSXW_V(vs3e))

  def VSE_V  (vs3e: String, rs1: String = rs1,                      vm: String = vm) = ("000"+"000"+vm+"00000"+rs1+"111"+vs3e+"0100111", "VSE_V      ")
  def VSSE_V (vs3e: String, rs1: String = rs1, rs2:  String = rs2,  vm: String = vm) = ("000"+"010"+vm+rs2    +rs1+"111"+vs3e+"0100111", "VSSE_V     ")
  def VSXE_V (vs3e: String, rs1: String = rs1, vs2e: String = vs2e, vm: String = vm) = ("000"+"011"+vm+vs2e   +rs1+"111"+vs3e+"0100111", "VSXE_V     ")
  def SEInsts(vs3e: String) = Array(VSE_V(vs3e), VSSE_V(vs3e), VSXE_V(vs3e))
///////////////////////////instructions name-code pairs/////////////////////////

  def InstHex(inst: BigInt) = "0"*(8-inst.toString(16).length)+inst.toString(16)

  //delay x cycles, under 8 cycles
  step(rnd.nextInt(8))

/////////////////////////////////////CSR Inst///////////////////////////////////
  for(i <- 0 until 1) {
    val (csrCode, csrName) = CSRInsts(rnd.nextInt(2))
    val csrInst = BigInt(csrCode, 2)

    writer.println("CSRInstName  = " + csrName + " CSRInst = " + InstHex(csrInst))
    writer.println()

    //poke a CSR instruction
    poke(c.io.core.req.valid, 1)
    poke(c.io.core.req.bits.inst, csrInst)
    step(1)
    poke(c.io.core.fromXData1, AVL)
    poke(c.io.core.fromXData2, vtype)
  }
/////////////////////////////////////CSR Inst///////////////////////////////////

///////////////////////////////////Load Insts///////////////////////////////////
  var j = 0
  for(i <- 0 until 2) {
    val vde              = ldDstField(i)
    val LdInsts          = if(SEW == 8) LBInsts(vde) ++ LEInsts(vde)
                           else if(SEW == 16) LBInsts(vde) ++ LHInsts(vde) ++ LEInsts(vde)
                           else if(SEW == 32) LBInsts(vde) ++ LHInsts(vde) ++ LWInsts(vde) ++ LEInsts(vde)
                           else LEInsts(vde)
    val ldIndex          = if(SEW == 8) rnd.nextInt(9)
                           else if(SEW == 16) rnd.nextInt(15)
                           else if(SEW == 32) rnd.nextInt(21)
                           else rnd.nextInt(3)
    val (ldCode, ldName) = LdInsts(ldIndex)
    val ldInst           = BigInt(ldCode, 2)
    val baseAddr         = BigInt(5, scala.util.Random)
    val strideConst      = BigInt(5, scala.util.Random)

    writer.println("LoadInstName = " + ldName + " LoadInst = " + InstHex(ldInst) + " vm = 1" + " vd = " + BigInt(vde, 2))

    //poke load instruction
    poke(c.io.core.req.valid, 1)
    poke(c.io.core.req.bits.inst, ldInst)
    step(1)
    poke(c.io.core.fromXData1, baseAddr)
    poke(c.io.core.fromXData2, strideConst)
    step(1)

    var storeReq   = Array((BigInt(0),BigInt(0),BigInt(0)),(BigInt(0),BigInt(0),BigInt(0)))
    //poke data
    while(peek(c.io.core.req.ready) == 0) {
      val reqValid: BigInt = peek(c.io.reqValid)
      val reqTag: BigInt   = peek(c.io.reqTag)
      val reqSize: BigInt  = peek(c.io.reqSize)
      val respHasData      = peek(c.io.reqCmd)+1
      val respData         = elemVal + j
      val reqReady         = 1
      val s2Nack           = 0
      storeReq = storeReq ++ Array((reqValid,reqTag,reqSize))

      poke(c.io.respValid, storeReq(0)._1)
      poke(c.io.respTag, storeReq(0)._2)
      poke(c.io.respSize, storeReq(0)._3)
      poke(c.io.respHasData, respHasData)
      poke(c.io.respData, respData)
      poke(c.io.reqReady, reqReady)
      poke(c.io.s2Nack, s2Nack)
      step(1)
      j = j + storeReq(0)._1.toInt
      storeReq = storeReq drop 1
    }
  }
///////////////////////////////////Load Insts///////////////////////////////////

  writer.println()

/////////////////////////////////////AL Insts///////////////////////////////////
  for(i <- 0 until 8) {
    val baseALInsts      = addInsts ++ waddInsts ++ adcInsts ++ bitInsts ++ shiftInsts ++ nshiftInsts ++ cmpInsts ++ minInsts ++ mergeInsts
    val maskInsts        = popcInst ++ firstInst ++ midxInsts ++ iotaInst ++ idInst ++ mbitInsts
//    val alInsts          = baseALInsts ++ maskInsts ++
//                           (if(c.MULDIV) mulInsts ++ wmulInsts ++ divInsts else Nil) ++
//                           (if(c.MULADD) maddInsts ++ wmaddInsts else Nil) ++
//                           (if(c.RED) redInsts else Nil) ++
//                           (if(c.MV) mvInsts else Nil)
    val alInsts          = redInsts
    val alInstSum        = alInsts.length
    val (alCode, alName) = alInsts(rnd.nextInt(alInstSum))
    val alInst           = BigInt(alCode, 2)
    val alDstField       = (alInst & BigInt("00000000000000000000111110000000", 2)) >> 7
    val alSrc1Field      = (alInst & BigInt("00000000000011111000000000000000", 2)) >> 15
    val alSrc2Field      = (alInst & BigInt("00000001111100000000000000000000", 2)) >> 20
    val alVmField        = (alInst & BigInt("00000010000000000000000000000000", 2)) >> 25
    val alDstFieldStr    = " "*(2-alDstField.toString.length) + alDstField.toString
    val alSrc1FieldStr   = " "*(2-alSrc1Field.toString.length) + alSrc1Field.toString
    val alSrc2FieldStr   = " "*(2-alSrc2Field.toString.length) + alSrc2Field.toString

  writer.println("ALInstName = " + alName + " ALInst = " + InstHex(alInst) + " DstField = " + alDstFieldStr + " Src2Field = " + alSrc2FieldStr + " Src1Field = " + alSrc1FieldStr + " vm = " + alVmField)

    //poke a AL instruction
    poke(c.io.core.req.valid, 1)
    poke(c.io.core.req.bits.inst, alInst)
    step(1)
    while(peek(c.io.core.req.ready) == 0)
      step(1)
  }
/////////////////////////////////////AL Insts///////////////////////////////////

  writer.println()

//////////////////////////////////Store Insts///////////////////////////////////
  for(i <- 0 until 1) {
    val stInsts          = if(SEW == 8) SBInsts(vs3e) ++ SEInsts(vs3e)
                           else if(SEW == 16) SBInsts(vs3e) ++ SHInsts(vs3e) ++ SEInsts(vs3e)
                           else if(SEW == 32) SBInsts(vs3e) ++ SHInsts(vs3e) ++ SWInsts(vs3e) ++ SEInsts(vs3e)
                           else SEInsts(vs3e)
    val stInstSum        = stInsts.length
    val (stCode, stName) = stInsts(rnd.nextInt(stInstSum))
    val stInst           = BigInt(stCode, 2)
    val stDstField       = (stInst & BigInt("00000000000000000000111110000000", 2)) >> 7
    val stSrc1Field      = (stInst & BigInt("00000000000011111000000000000000", 2)) >> 15
    val stSrc2Field      = (stInst & BigInt("00000001111100000000000000000000", 2)) >> 20
    val stVmField        = (stInst & BigInt("00000010000000000000000000000000", 2)) >> 25
    val stDstFieldStr    = " "*(2-stDstField.toString.length) + stDstField.toString
    val stSrc1FieldStr   = " "*(2-stSrc1Field.toString.length) + stSrc1Field.toString
    val stSrc2FieldStr   = " "*(2-stSrc2Field.toString.length) + stSrc2Field.toString

    writer.println("StoreInstName = " + stName + " StoreInst = " + InstHex(stInst) + " vs3 = " + stDstFieldStr + " rs1 = " + stSrc1FieldStr + " src2Field = " + stSrc2FieldStr + " vm = " + stVmField)

    //poke a store instruction
    poke(c.io.core.req.valid, 1)
    poke(c.io.core.req.bits.inst, stInst)
    step(1)
/*
    for(k <- 0 until AVL) {
        poke(c.io.respValid, 1)
        poke(c.io.respTag, peek(c.io.reqTag))
        poke(c.io.respSize, peek(c.io.reqSize))
        poke(c.io.respHasData, 0)
        poke(c.io.respData, 0)
        poke(c.io.reqReady, 1)
        poke(c.io.s2Nack, 0)
      step(1)
    }
*/
    var storeReq   = Array((BigInt(0),BigInt(0),BigInt(0)),(BigInt(0),BigInt(0),BigInt(0)))
    //poke data
    while(peek(c.io.core.req.ready) == 0) {
      val reqValid: BigInt = peek(c.io.reqValid)
      val reqTag: BigInt   = peek(c.io.reqTag)
      val reqSize: BigInt  = peek(c.io.reqSize)
      val respHasData      = peek(c.io.reqCmd)+1
      val respData         = elemVal + j
      val reqReady         = 1
      val s2Nack           = 0
      storeReq = storeReq ++ Array((reqValid,reqTag,reqSize))

      poke(c.io.respValid, storeReq(0)._1)
      poke(c.io.respTag, storeReq(0)._2)
      poke(c.io.respSize, storeReq(0)._3)
      poke(c.io.respHasData, respHasData)
      poke(c.io.respData, respData)
      poke(c.io.reqReady, reqReady)
      poke(c.io.s2Nack, s2Nack)
      step(1)
      j = j + storeReq(0)._1.toInt
      storeReq = storeReq drop 1
    }
  }
//////////////////////////////////Store Insts///////////////////////////////////
  poke(c.io.core.req.valid, 0)
  step(5)
  writer.close()
}

def VLXH_V (vde: String,  rs1: String = rs1, vs2e: String = vs2e, vm: String = "1") = ("000"+"111"+vm+vs2e   +rs1+"101"+vde+"0000111", "VLXH_V     ")


package day24
import chisel3._
import chisel3.util._

  // 3 bit input
  val select = Input(UInt(3.W))
  // 32 bit output
  val out = Output (UInt (32.W))
}
class Mux_Case extends Module {
  val io = IO (new LM_IO_Interface )
  //when the select pin matches with any of the condition that value is returned from the mux case
  // if no match is found the 0 is returned as the default value
  io.out := MuxCase(0.U, Array(
    (io.select === "b000".U) -> 0.U,
    (io.select === "b001".U) -> 8.U,
    (io.select === "b010".U) -> 16.U,
    (io.select === "b011".U) -> 24.U,
    (io.select === "b100".U) -> 32.U,
  ) )

}


package day26

import chisel3._
import chisel3.util._
import chisel3.stage.ChiselStage

class LM_IO_Interface_BranchControl extends Bundle {
    val fnct3 = Input ( UInt (3.W ) )
    val branch = Input ( Bool () )
    val arg_x = Input ( UInt (4.W ) )
    val arg_y = Input ( UInt (4.W ) )
    val br_taken = Output ( Bool () )
}

class BranchControl extends Module{
    val io = IO (new LM_IO_Interface_BranchControl)

when( io.branch === 1.B ){                           // Check if the branch signal is high

        io.br_taken :=  io.arg_x < io.arg_y          // Branch taken if arg_x is less than arg_y
    }.elsewhen ( io.fnct3 === 5.U ) {
        io.br_taken :=  io.arg_x > io.arg_y          // Branch taken if arg_x is greater than arg_y
    }.otherwise{
        io.br_taken := 0.B                           // Default case: branch not taken
    }

}.otherwise{
    io.br_taken := 0.B
}

}

    when ( io.fnct3 === 0.U ) {
        io.br_taken :=  io.arg_x === io.arg_y        // Branch taken if arg_x is equal to arg_y
    }.elsewhen ( io.fnct3 === 1.U ) {


package day27

import chisel3._
import chisel3.util._

// IO definition for the Immediate Value Generator

  val s = 35.U(7.W)  // s-type opcode defined as per RISC-V standard
  val sb = 99.U(7.W) // sb-type opcode defined as per RISC-V standard
  val u = 55.U(7.W)  // u-type opcode defined as per RISC-V standard
  val uj = 111.U(7.W)// uj-type opcode defined as per RISC-V standard
  val ei = 0.U(32.W)  // Extra immediate, defined as zero in immediate
}

// Module for Immediate Extension
class ImmExtension extends Module {
  val io = IO(new LM_IO_Interface_ImmdValGen)

  // Immediate extension logic using Mux statements
  io.immd_se := Mux(
    io.instr(6, 0) === imm.i || io.instr(6, 0) === "b0000011".U || io.instr(6, 0) === "b1100111".U,
    Cat(imm.ei(19, 0), io.instr(31, 20)),
    Mux(
      io.instr(6, 0) === imm.s,
      Cat(imm.ei(19, 0), io.instr(31, 25), io.instr(11, 7)),
      Mux(
        io.instr(6, 0) === imm.sb,
        Cat(imm.ei(19, 0), io.instr(31), io.instr(7), io.instr(30, 25), io.instr(11, 8), "b0".U) + (io.pc_in).asUInt,
        Mux(
          io.instr(6, 0) === imm.u,
          Cat(io.instr(31, 12), imm.ei(11, 0)),
          Mux(
            io.instr(6, 0) === imm.uj,
            Cat(imm.ei(11, 0), io.instr(31), io.instr(19, 12), io.instr(20), io.instr(30, 21), "b0".U) + (io.pc_in).asUInt,
            0.U
          ).asUInt
        )
      )
    )
  )
}

class LM_IO_Interface_ImmdValGen extends Bundle {
  val instr = Input(UInt(32.W))    // 32-bit instruction input
  val pc_in = Input(SInt(32.W))    // 32-bit signed program counter input
  val immd_se = Output(UInt(32.W)) // 32-bit signed immediate output
}


package day29
import chisel3._
import chisel3.util._

class Arbriter extends Module{
    val io = IO (new Bundle {
        val in1 = Flipped (Decoupled(UInt(4.W)))
        val in2 = Flipped (Decoupled(UInt(4.W)))
        val out = Decoupled(UInt(4.W))

val arb_priority = Module (new Arbiter ( UInt () , 2) )

arb_priority.io.in(0) <> queue1
arb_priority.io.in(1) <> queue2

io.out <> arb_priority.io.out
}


package day3

import chisel3._

class Bitwidth extends Module {
    val io = IO(new Bundle {           //Bundle Definition
        val input = Input(UInt(4.W))   //Datatype

    val temp = Wire(SInt(4.W)) //wiring
    temp := io.input.asSInt    //typecasting
    io.output := temp
}

        val output = Output(SInt(4.W))
    })


package day1
import chisel3._
import org.scalatest._
import chiseltest._

class GatesTests extends FreeSpec with ChiselScalatestTester {
    "Gates Test" in {
        test(new Gates()) {c =>
            c.io.a.poke(1.U)

            c.clock.step(3)

            c.io.AND.expect(0.U)
            c.io.OR.expect(3.U)
            c.io.NOT.expect(14.U)
            c.io.NOR.expect(12.U)
            c.io.XOR.expect(3.U)
            c.io.XNOR.expect(12.U)
        }
    }
}


package day14
import org.scalatest._
import chiseltest._
import chisel3._

class Adder_param_Test extends FreeSpec with ChiselScalatestTester{
    "Adder_param" in  {

            test(new Adder(32)){c=>
            c.io.in_0.poke(12.U)
            c.io.in_1.poke(4.U)
        c.clock.step(100)}


package day18
import chisel3._
import org.scalatest.FreeSpec
import chiseltest._

class My_QueueTest extends FreeSpec with ChiselScalatestTester {
    "QueueTest" in {
        test(new My_Queue) { c =>
            // Test case description: Test the My_Queue module.

            // Set the input data to 4 (bits) and indicate that it's valid.
            c.io.in.bits.poke(4.U)
            c.io.in.valid.poke(1.B)

            // Advance the simulation clock by 5 cycles.

            c.clock.step(5)

            // Expect that the output bits should be 4.


package day19

import chisel3._
import org.scalatest.FreeSpec
import chiseltest._

class OneShotTimerTest extends FreeSpec with ChiselScalatestTester{
   "OneShotTimerTest test" in{

       test(new OneShotTimerModule()){c =>
         c.clock.step(10)
      }


package day23

import chisel3._
import chisel3.tester._
import org.scalatest.FreeSpec
import chisel3.experimental.BundleLiterals._

class Mux_2to1test extends FreeSpec with ChiselScalatestTester{
  "lab 2 ex1Tester " in {
    test(new Mux_2to1){ a=>
      a.io.in_A.poke(45.U) // input A
      a.io.in_B.poke(55.U) // input B
      a.io.select.poke(1.B) // select pin selects which input to send as output
      a.io.out.expect(45.U) // output received


package day26

import chisel3._
import org.scalatest.FreeSpec
import chiseltest._

// Define a test class for the BranchControl module
class branch_controlTest extends FreeSpec with ChiselScalatestTester{
   "branch_controlTest test" in{

       test(new BranchControl()){c =>
         c.io.fnct3.poke(1.U)              // Set input signals for the BranchControl module
         c.io.branch.poke(1.B)
         c.io.arg_x.poke(1.U)
         c.io.arg_y.poke(0.U)


package chiselverify.assembly

import java.nio.file.{Files, Paths}

class ProgramBinaries(n: String, bin: Array[Int], len: Int) {
  val name = n
  val byteBinaries = bin
  val wordBinaries = bin.map(BigInt(_)).sliding(4, 4).map(a => a(3) << 24 | a(2) << 16 | a(1) << 8 | a(0)).toArray
  val length = len
}

object BinaryLoader {
  def loadProgram(path: String): ProgramBinaries = {
    val (bin, length) = loadBin(path)
    val split = path.split(Array('/', '\\'))
    val name = split(split.length - 1)
    new ProgramBinaries(name.substring(0, name.length - 4), bin, length)
  }

  def loadBin(path: String): (Array[Int], Int) = {
    val bytes = Files.readAllBytes(Paths.get(path)).map(_ & 0xFF).sliding(4, 4).toArray

    (Array.concat(bytes.flatten, Array.fill(16)(0)), bytes.flatten.length / 4)
  }
}

if (bytes(bytes.length - 1).length < 4) bytes(bytes.length - 1) = Array(0, 0, 0, 0)


package chiselverify.assembly

/**
  * Functions for creating symbolic labels in generated assembly code
  * they are represented by instruction objects whose assembly code is
  * the label itself
  */
object Label {

  case class LabelRecord(id: String)

  // auto generated label factory
  def apply(): InstructionFactory with Categorizable = {
    Pattern(Category.Label)(implicit c => {
      val lbl = s"RANDOM_LABEL_${c.labelCounter.inc()}"
      c.jumpTargets.append(LabelRecord(lbl))
      Seq(Label.create(lbl))
    })
  }

  // user defined labels factory
  def apply(lbl: String): Pattern = {
    Pattern(implicit c => {
      c.jumpTargets.append(LabelRecord(lbl))
      Seq(Label.create(lbl))
    })
  }

  // create a anonymous instruction instance with the label name set as the assembly representation
  private def create(id: String): Instruction = {
    new Instruction(Category.Label) {
      override def apply(): Instruction = Label.create(id)


package chiselverify.assembly

import chiselverify.assembly.Label.LabelRecord
import chiselverify.assembly.RandomHelpers.{rand, randomSelect}
import chiselverify.assembly.AssemblyDistributions._

import java.io.{File, PrintWriter}
import scala.collection.mutable.ListBuffer
import scala.math.BigInt
import scala.util.Random

/**
  * The generator context is used to collect information required during the generation process centrally
  * It holds all "sampler" functions which produce new values for instructions, memory addresses etc.
  * Global state like defined labels and the current PC are tracked here.
  * An instance of a generator context is passed down the function call tree which produces the final instruction sequence
  */
case class GeneratorContext(
  isa: InstructionSet,
  nextInstruction: Seq[Constraint] => InstructionFactory with Categorizable,
  nextMemoryAddress: Seq[Constraint] => BigInt,
  nextIOAddress: Seq[Constraint] => BigInt,
  nextJumpTarget: () => LabelRecord,
  pc: Counter,
  labelCounter: Counter,
  jumpTargets: ListBuffer[LabelRecord]
)

object GeneratorContext {
  // a generator context can be defined by an ISA and a set of constraints
  def apply(isa: InstructionSet, constraints: Seq[Constraint]): GeneratorContext ={
    val pc = new Counter
    val targets = new ListBuffer[LabelRecord]()
    GeneratorContext(
      isa,
      createInstructionGenerator(isa, constraints),
      createMemoryAddressGenerator(isa, constraints),
      createIOAddressGenerator(isa, constraints),
      createJumpTargetGenerator(pc,targets),
      pc,
      new Counter,
      targets
    )
  }

  /**
    * The instruction generator gives access to a distribution driven instruction stream.
    * Additional white and black list constraints can be added when sampling.
    */
  private def createInstructionGenerator(isa: InstructionSet, constraints: Seq[Constraint]): Seq[Constraint] => InstructionFactory with Categorizable = { additionalConstraints =>
    // get all applicable instruction constraints
    val allCons = (constraints ++ additionalConstraints).collect { case x: InstructionConstraint => x }

    // apply black and white lists to find allowed categories
    val allowedCategories: Seq[Category] = allCons.foldLeft(Category.all) {
      case (cats, CategoryBlackList(bl@_*)) => cats.filter(!bl.contains(_))
      case (cats, CategoryWhiteList(w@_*)) => cats.filter(w.contains(_))
      case (cats, _) => cats
    }

    val blacklisted = allCons.collect { case CategoryBlackList(bl@_*) => bl}.flatten

    // find all distribution constraints
    val distConstraints = allCons.collect { case x: CategoryDistribution => x }.flatMap(_.dis)

    if (distConstraints.nonEmpty) {
      // sort out black listed categories
      val filtered = distConstraints.filter { case (c, _) => allowedCategories.contains(c) }

      // add the label category, to always make random labels appear
      val withLabel = filtered ++ Seq((Category.Label,filtered.map(_._2).sum * 0.15))

      // create uniform distributions with all applicable instructions for each category
      val distributionConstraints = withLabel.map { case (c, d) =>
        val instructions = (isa.instructions ++ Seq(Label())).filter(i => i.isOfCategory(c) && !i.isOfOneOfCategories(blacklisted))
        (instructions, d)
      }.filter(_._1.nonEmpty).map { case (instr, d) =>
        (discreteUniform(instr).sample(1).head, d)
      }

      // create the top level distribution which contains the category weights
      discrete(distributionConstraints: _*).sample(1).head
    } else {
      // no distribution was supplied and all allowed instructions are distributed uniformly
      randomSelect((isa.instructions ++ Seq(Label())).filter(i => i.isOfOneOfCategories(allowedCategories) && !i.isOfOneOfCategories(blacklisted)))
    }
  }

  /**
    * The memory address generator can be sampled to produce new memory addresses.
    * A distribution can be defined when the sampler is created.
    */
  private def createMemoryAddressGenerator(isa: InstructionSet, constraints: Seq[Constraint]): Seq[Constraint] => BigInt = { additionalConstraints =>
    val dis = constraints.collect { case x: MemoryDistribution => x }.toList match {
      case x :: _ => x.dis
      case _ => Seq(isa.memoryAddressSpace -> 1.0)
    }
    discrete(dis.map { case (r, d) => (rand(r), d) }: _*).sample(1).head
  }

  /**
    * The I/O address generator can be sampled to produce new I/O port addresses.
    * A distribution can be defined when the samples is created.
    */
  private def createIOAddressGenerator(isa: InstructionSet, constraints: Seq[Constraint]): Seq[Constraint] => BigInt = { additionalConstraints =>
    val dis = constraints.collect { case x: IODistribution => x }.toList match {
      case x :: _ => x.dis
      case _ => Seq(isa.inputOutputAddressSpace -> 1.0)
    }
    discrete(dis.map { case (r, d) => (rand(r), d) }: _*).sample(1).head
  }

  /**
    * The jump target generator produces references to defined symbolic labels in the produced assembly code
    */
  private def createJumpTargetGenerator(pc: Counter, targets: ListBuffer[LabelRecord]): () => LabelRecord = { () =>
    if(targets.nonEmpty) randomSelect(targets.toSeq) else LabelRecord("RANDOM_LABEL_0")
  }
}

object ProgramGenerator {
  def apply(isa: InstructionSet)(constraints: Constraint*): ProgramGenerator = {
    new ProgramGenerator(GeneratorContext(isa, constraints))
  }
}

/**
  * Wrapper for the generated instruction sequence.
  *
  * Gives access to some distribution statistics and printing as well as saving to file methods
  */
case class Program(instructions: Seq[Instruction], isa: InstructionSet) {
  // returns the assembly code
  override def toString: String = instructions.map(_.toAsm).map { str =>
    if (str.contains(':')) str else "  " + str
  }.mkString("\n")

  // assembly code + instruction and category histograms
  def pretty: String = {

  }

  // the number of occurrences for each instruction in the program
  def histogram: Seq[(String, Int)] = {
    instructions
      .map(_.toAsm.split(" ").head)
      .filter(!_.contains(':'))
      .distinct.map { instr =>
      instr -> instructions.count(_.toAsm.split(" ").head == instr)
    }
  }

  // the number of instructions matching the different categories
  def categoryHistogram: Seq[(String, Int)] = {
    Category.all.map { c =>
      (c.toString, instructions.count(_.categories.contains(c)))
    }
  }

  // export the assembly code to a file
  def saveToFile(fileName: String): Unit = {
    val writer = new PrintWriter(new File(fileName))
    writer.write(toString+"\n")
    writer.close()
  }
}

/**
  * A program generator object is characterized by its constraints which are applied to every
  * generation call. A specific seed can be provided when generating a new sequence.
  */
class ProgramGenerator(context: GeneratorContext) {

  // generate approximately n instructions using the passed seed
  def generate(n: Int, seed: Long): Program = {
    Random.setSeed(seed)
    Program(Seq.fill(n)(context.nextInstruction(Seq()).produce()(context)).flatten, context.isa)
  }

  // generate approximately n instructions using a random seed
  def generate(n: Int): Program = {
    generate(n, Random.nextLong())
  }

  // generate an instruction sequence based on a pattern using the passed seed
  def generate(p: Pattern, seed: Long): Program = {
    Random.setSeed(seed)
    Program(p.produce()(context), context.isa)
  }

  // generate an instruction sequence based on a pattern with a random seed
  def generate(p: Pattern): Program = {
    generate(p, Random.nextLong())
  }
}


package chiselverify.crv

trait RandObj {
  /** Randomize the current object
    * @return Boolean returns true only if a solution was found
    */
  def randomize: Boolean

  /** Randomize the current object with additional [[Constraint]]
    * @return Boolean returns true only if a solution was found
    */
  def randomizeWith(constraints: CRVConstraint*): Boolean

  /** Method containing a set of directives to run before the current object is randomized
    */

  /** Method containing a set of directives to run after the current object is randomized
    */
  def postRandomize(): Unit = {}
}


package chiselverify.crv.backends.jacop

import scala.collection.immutable
import scala.util.Random

import chiselverify.crv.CRVException

trait DistTrait {
  protected val random: Random
  protected val wConstraintGList: Seq[WConstraintGroup]
  protected val _var: Rand
  /**
    * Like for normal constraints, the distribution constraint can be enabled or disabled
    */
  var isEnabled = true

  /**
    * Disables the constraint
    */
  def disable(): Unit = {
    isEnabled = false
  }

  /**
    * Enables the constraint
    *
    * Each constraint is by default enabled when instantiated. This method has effect only if called after [[disable]]
    */
  def enable(): Unit = {
    isEnabled = true
  }

  /**
    * Randomly enables one of the constraint groups defined in the constraint group list
    */
  def randomlyEnable(): Unit = {
    val number = random.nextDouble()
    wConstraintGList.find(x => x.contains(number)) match {

      case Some(x) => x.enable()
    }
  }

  /**
    * Disables all the constraints in the constraint group list
    */
  def disableAll(): Unit = wConstraintGList foreach (_.disable())

  /**
    * From a list of doubles creates a list of buckets where the min is the current double and the max is the next element on the list
   */
  protected def swipeAndSum(buckets: List[Double]): List[Bucket] = {
    buckets match {
      case Nil => Nil
      case _ :: Nil => Nil
      case x :: xs => Bucket(x, xs.head) :: swipeAndSum(xs)
    }
  }
}

/**
  * Create a [[DistConstraint]] between a [[Rand]] and a list of [[WeightedRange]] and [[WeightedValue]]
  * @param variable the [[Rand]] variable to constrain
  * @param listOfDistC list of [[WeightedRange]] and [[WeightedValue]]
  * @param model implicit [[Model]] of the current [[RandObj]]
  */
class DistConstraint(variable: Rand, listOfDistC: List[Weight])(implicit model: Model) extends DistTrait {
  protected override val _var: Rand = variable
  protected override val random = new Random(model.seed + listOfDistC.length)

  /**
    * Creates a [[WConstraintGroup]] based on the type of [[Weight]] and [[Bucket]]
    * @param rangeAndBucket tuple of [[Weight]] and [[Bucket]]
    * @return the defined constraint group
    */
  private def createWeightedConstraintGroup(rangeAndBucket: (Weight, Bucket)) = {
    rangeAndBucket match {
      case (weight, bucket) => weight match {
        case WeightedRange(range, _) => new WConstraintGroup(bucket, variable > range.start, variable <= range.end)
        case WeightedValue(value, _) => new WConstraintGroup(bucket, variable == value)
      }
    }
  }

  /**
    * Constraint group list
    */
  protected override val wConstraintGList = {
    val total: Double = listOfDistC.map(_.weight).sum.toDouble
    val buckets:  List[Double] = listOfDistC.map(_.weight / total).scan(0.0)((a, b) => a + b)
    val zipped  = listOfDistC zip swipeAndSum(buckets)
    zipped.map(createWeightedConstraintGroup)
  }
}

case None => throw CRVException(s"Something went wrong in the distribution selection of ${_var.toString}")


package chiselverify.crv.backends.jacop

import org.jacop.constraints.PrimitiveConstraint

/**
  * Since not all csp solvers have support for conditional constraints, the IfThen and IfThenElse are not part of the common crv package.
  */
class IfCon(val const: JaCoPConstraint, val ifC: chiselverify.crv.CRVConstraint, val thenC: chiselverify.crv.CRVConstraint)(implicit model: Model)
  extends JaCoPConstraint(const.getConstraint) {

  /**
    * Companion class for creating if-then-else constraint
    * @param ifCons an IfConstraint
    * @param elseC an ElseConstraint
    */
  class IfElseCon(val ifCons: IfCon, val elseC: chiselverify.crv.CRVConstraint) extends IfCon(const, ifC, thenC) {
    val newConstraint = new org.jacop.constraints.IfThenElse(
      ifC.getConstraint.asInstanceOf[PrimitiveConstraint],
      thenC.getConstraint.asInstanceOf[PrimitiveConstraint],
      elseC.getConstraint.asInstanceOf[PrimitiveConstraint]
    )
    val crvc = new JaCoPConstraint(newConstraint)
    model.crvconstr += crvc
    elseC.disable()

      crvc.enable()
    }
  }

  override def enable(): Unit = {
    const.enable()
  }

  override def disable(): Unit = {
    const.disable()
  }

  /** Create an if-then-else constraint
    * @param elseC the constraint to be applied if the ifC condition is NOT true
    * @return
    */
  def ElseC(elseC: chiselverify.crv.CRVConstraint): IfElseCon = {
    const.disable()
    new IfElseCon(this, elseC)
  }

  // Initialization conde
  model.crvconstr += const
  ifC.disable()
  thenC.disable()
}

/**
  * Helper object for defining IfThen constraints
  */
object IfCon {
  /**
    * Creates a new IfThen constraint
    * @param ifC the if condition represented as a constraint
    * @param thenC the constraint to be applied if the ifC condition is true
    * @param model the current [[Model]], constraint can only be defined inside a [[RandObj]]
    * @return return the newly constructed [[Constraint]]
    */
  def apply(ifC: chiselverify.crv.CRVConstraint)(thenC: chiselverify.crv.CRVConstraint)(implicit model: Model): IfCon = {
    val newConstraint =
      new org.jacop.constraints.IfThen(
        ifC.getConstraint.asInstanceOf[PrimitiveConstraint],
        thenC.getConstraint.asInstanceOf[PrimitiveConstraint]
      )
    val crvc = new JaCoPConstraint(newConstraint)
    new IfCon(crvc, ifC, thenC)
  }
}

    override def disable(): Unit = {
      crvc.disable()
    }


package chiselverify.crv.backends

import org.jacop.constraints._
import org.jacop.core.IntDomain
import org.jacop.scala.{IntSet, SetVar}
import org.jacop.set.constraints.{EinA, XinA}
import scala.util.Random

import chiselverify.crv.randName

package object jacop {
  type RandVar = Rand
  type RandCVar = Randc
  private[crv] class Rand(name: String, min: Int, max: Int)(implicit val model: Model)
    extends org.jacop.core.IntVar(model, name, min, max)
      with chiselverify.crv.Rand {

    override type U = Rand

    /** Defines an anonymous finite domain integer variable.
      *
      * @constructor Creates a new finite domain integer variable.
      * @param min minimal value of variable's domain.
      * @param max maximal value of variable's domain.
      */
    def this(min: Int, max: Int)(implicit model: Model) = {
      this("_$" + model.n, min, max)(model)
      model.n += 1
    }

    /** Defines an anonymous finite domain integer variable.
      *
      * @constructor Creates a new finite domain integer variable with minimal and maximal
      *              values in the domain defined by [[org.jacop]]
      * @param name variable's identifier.
      */
    def this(name: String)(implicit model: Model) = {
      this(name, org.jacop.core.IntDomain.MinInt, org.jacop.core.IntDomain.MaxInt)(model)
      model.n += 1
    }

    /** Defines an anonymous finite domain integer variable.
      *
      * @constructor Creates a new finite domain integer variable with minimal and maximal
      *              values in the domain defined by [[org.jacop]]
      */
    def this()(implicit model: Model) = {
      this(org.jacop.core.IntDomain.MinInt, org.jacop.core.IntDomain.MaxInt)(model)
      model.n += 1
    }

    /** Defines a finite domain integer variable.
      *
      * @constructor Create a new finite domain integer variable with the domain defined by IntSet.
      * @param dom variable's domain defined as a set of integers IntSet.
      */
    def this(dom: IntSet)(implicit model: Model) = {
      this()(model)
      this.dom.intersectAdapt(dom)
      model.n += 1
    }

    /** Defines a finite domain integer variable.
      *
      * @constructor Create a new finite domain integer variable with the domain
      *              defined by IntSet.
      * @param name variable's identifier.
      * @param dom  variable's domain defined as a set of IntSet.
      */
    def this(name: String, dom: IntSet)(implicit model: Model) = {
      this(name)(model)
      this.dom.intersectAdapt(dom)
      model.n += 1
    }

    /** Assign a specific value to the current variable
      * @param v BigInt
      */
    def setVar(v: BigInt): Unit = {
      require(v < Int.MaxValue)
      setDomain(v.toInt, v.toInt)
    }

    /** Defines the add [[JaCoPConstraint]] between two Rand variables
      *
      * @param that a second parameter for the addition constraint
      * @return [[Rand]] variable being the result of the addition [[JaCoPConstraint]].
      */
    def +(that: Rand): Rand = {
      val result = new Rand(IntDomain.addInt(this.min(), that.min()), IntDomain.addInt(this.max(), that.max()))
      val c = new XplusYeqZ(this, that, result)
      model.crvconstr += new JaCoPConstraint(c)
      result
    }

    /** Defines add [[JaCoPConstraint]] between Rand and an integer value.
      *
      * @param that a second integer parameter for the addition [[JaCoPConstraint]].
      * @return [[Rand]] variable being the result of the addition [[JaCoPConstraint]].
      */
    def +(that: BigInt): Rand = {
      require(that <= Int.MaxValue)
      val result = new Rand(IntDomain.addInt(this.min(), that.toInt), IntDomain.addInt(this.max(), that.toInt))
      val c = new XplusCeqZ(this, that.toInt, result)
      model.crvconstr += new JaCoPConstraint(c)
      result
    }

    /** Defines subtract [[JaCoPConstraint]] between two Rand.
      *
      * @param that a second parameter for the subtraction [[JaCoPConstraint]].
      * @return [[Rand]] variable being the result of the subtraction [[JaCoPConstraint]].
      */
    def -(that: Rand): Rand = {
      val result = new Rand(IntDomain.subtractInt(this.min(), that.max()), IntDomain.subtractInt(this.max(), that.min()))
      val c = new XplusYeqZ(result, that, this)
      model.crvconstr += new JaCoPConstraint(c)
      result
    }

    /** Defines subtract [[JaCoPConstraint]] between [[Rand]] and an integer value.
      *
      * @param that a second integer parameter for the subtraction [[JaCoPConstraint]].
      * @return [[Rand]] variable being the result of the subtraction [[JaCoPConstraint]].
      */
    def -(that: BigInt): Rand = {
      require(that <= Int.MaxValue)
      val result = new Rand(IntDomain.subtractInt(this.min(), that.toInt), IntDomain.subtractInt(this.max(), that.toInt))
      val c = new XplusCeqZ(result, that.toInt, this)
      val crvc = new JaCoPConstraint(c)
      model.crvconstr += crvc
      result
    }

    /** Defines equation [[JaCoPConstraint]] between two [[Rand]].
      *
      * @param that a second parameter for equation [[JaCoPConstraint]].
      * @return the defined [[JaCoPConstraint]].
      */
    def ==(that: Rand): JaCoPConstraint = {
      val c = new XeqY(this, that)
      val crvc = new JaCoPConstraint(c)
      model.crvconstr += crvc
      crvc
    }

    /** Defines equation [[JaCoPConstraint]] between [[Rand]] and a integer constant.
      *
      * @param that a second parameter for equation [[JaCoPConstraint]].
      * @return the defined [[JaCoPConstraint]].
      */
    def ==(that: BigInt): JaCoPConstraint = {
      require(that <= Int.MaxValue)
      val c = new XeqC(this, that.toInt)
      val crvc = new JaCoPConstraint(c)
      model.crvconstr += crvc
      crvc
    }
    def ==(that: Int): JaCoPConstraint = ==(BigInt(that))

    /** Defines multiplication [[JaCoPConstraint]] between two [[Rand]].
      *
      * @param that a second parameter for the multiplication [[JaCoPConstraint]].
      * @return [[Rand]] variable being the result of the multiplication [[JaCoPConstraint]].
      */
    def *(that: Rand): Rand = {
      val bounds = IntDomain.mulBounds(this.min(), this.max(), that.min(), that.max())
      val result = new Rand(bounds.min(), bounds.max())
      val c = new XmulYeqZ(this, that, result)
      model.crvconstr += new JaCoPConstraint(c)
      result
    }

    /** Defines multiplication [[JaCoPConstraint]] between [[Rand]] and an integer value.
      *
      * @param that a second integer parameter for the multiplication [[JaCoPConstraint]].
      * @return [[Rand]] variable being the result of the multiplication [[JaCoPConstraint]].
      */
    def *(that: BigInt): Rand = {
      require(that <= Int.MaxValue)
      val bounds = IntDomain.mulBounds(this.min(), this.max(), that.toInt, that.toInt)
      val result = new Rand(bounds.min(), bounds.max())
      val c = new XmulCeqZ(this, that.toInt, result)
      model.crvconstr += new JaCoPConstraint(c)
      result
    }

    /** Defines integer division [[JaCoPConstraint]] between two [[Rand]].
      *
      * @param that a second parameter for the integer division [[JaCoPConstraint]].
      * @return [[Rand]] variable being the result of the integer division [[JaCoPConstraint]].
      */
    def div(that: Rand): Rand = {
      val bounds = IntDomain.divBounds(this.min(), this.max(), that.min(), that.max())
      val result = new Rand(bounds.min(), bounds.max())
      val c = new XdivYeqZ(this, that, result)
      model.crvconstr += new JaCoPConstraint(c)
      result
    }

    /** Defines integer division [[JaCoPConstraint]] between [[Rand]] and an integer value.
      *
      * @param that a second parameter for the integer division [[JaCoPConstraint]].
      * @return [[Rand]] variable being the result of the integer division [[JaCoPConstraint]].
      */
    def div(that: BigInt): Rand = {
      require(that < Int.MaxValue)
      this.div(new Rand(that.toInt, that.toInt))
    }

    /** Defines [[JaCoPConstraint]] for integer reminder from division between two [[Rand]].
      *
      * @param that a second parameter for integer reminder from division [[JaCoPConstraint]].
      * @return [[Rand]] variable being the result of the integer reminder from division [[JaCoPConstraint]].
      */
    def mod(that: Rand): Rand = {
      var reminderMin: Int = 0
      var reminderMax: Int = 0

      if (this.min() >= 0) {
        reminderMin = 0
        reminderMax = Math.max(Math.abs(that.min()), Math.abs(that.max())) - 1
      } else if (this.max() < 0) {
        reminderMax = 0
        reminderMin = -Math.max(Math.abs(that.min()), Math.abs(that.max())) + 1
      } else {
        reminderMin = Math.min(Math.min(that.min(), -that.min()), Math.min(that.max(), -that.max())) + 1
        reminderMax = Math.max(Math.max(that.min(), -that.min()), Math.max(that.max(), -that.max())) - 1
      }

      val result = new Rand(reminderMin, reminderMax)
      val c = new XmodYeqZ(this, that, result)
      model.crvconstr += new JaCoPConstraint(c)
      result
    }

    /** Defines [[JaCoPConstraint]] for integer reminder from division [[Rand]] and an integer value.
      *
      * @param that a second parameter for integer reminder from division [[JaCoPConstraint]].
      * @return [[Rand]] variable being the result of the integer reminder from division [[JaCoPConstraint]].
      */
    def mod(that: BigInt): Rand = {
      require(that <= Int.MaxValue)
      this.mod(new Rand(that.toInt, that.toInt))
    }

    /** Defines exponentiation [[JaCoPConstraint]] between two [[Rand]].
      *
      * @param that exponent for the exponentiation [[JaCoPConstraint]].
      * @return [[Rand]] variable being the result of the exponentiation [[JaCoPConstraint]].
      */
    def ^(that: Rand): Rand = {
      val result = new Rand()
      val c = new XexpYeqZ(this, that, result)
      model.crvconstr += new JaCoPConstraint(c)
      result
    }

    /** Defines exponentiation [[JaCoPConstraint]] between [[Rand]] and an integer value.
      *
      * @param that exponent for the exponentiation [[JaCoPConstraint]].
      * @return [[Rand]] variable being the result of the exponentiation [[JaCoPConstraint]].
      */
    def ^(that: BigInt): Rand = {
      require(that <= Int.MaxValue)
      this ^ new Rand(that.toInt, that.toInt)
    }

    /** Defines unary "-" [[JaCoPConstraint]] for [[Rand]].
      *
      * @return the defined [[JaCoPConstraint]].
      */
    def unary_- : Rand = {
      val result = new Rand(-this.max(), -this.min())
      val c = new XplusYeqC(this, result, 0)
      model.crvconstr += new JaCoPConstraint(c)
      result
    }

    /** Defines inequality [[JaCoPConstraint]] between two [[Rand]].
      *
      * @param that a second parameter for inequality [[JaCoPConstraint]].
      * @return the defined [[JaCoPConstraint]].
      */
    def \=(that: Rand): JaCoPConstraint = {
      val c = new XneqY(this, that)
      val crvc = new JaCoPConstraint(c)
      model.crvconstr += crvc
      crvc
    }

    /** Defines inequality [[JaCoPConstraint]] between [[Rand]] and integer constant.
      *
      * @param that a second parameter for inequality [[JaCoPConstraint]].
      * @return the defined [[JaCoPConstraint]].
      */
    def \=(that: BigInt): JaCoPConstraint = {
      require(that <= Int.MaxValue)
      val c = new XneqC(this, that.toInt)
      val crvc = new JaCoPConstraint(c)
      model.crvconstr += crvc
      crvc
    }

    /** Defines "less than" [[JaCoPConstraint]] between two [[Rand]].
      *
      * @param that a second parameter for "less than" [[JaCoPConstraint]].
      * @return the defined [[JaCoPConstraint]].
      */
    def <(that: Rand): JaCoPConstraint = {
      val c = new XltY(this, that)
      val crvc = new JaCoPConstraint(c)
      model.crvconstr += crvc
      crvc
    }

    /** Defines "less than" [[JaCoPConstraint]] between [[Rand]] and integer constant.
      *
      * @param that a second parameter for "less than" [[JaCoPConstraint]].
      * @return the equation [[JaCoPConstraint]].
      */
    def <(that: BigInt): JaCoPConstraint = {
      require(that <= Int.MaxValue)
      val c = new XltC(this, that.toInt)
      val crvc = new JaCoPConstraint(c)
      model.crvconstr += crvc
      crvc
    }

    /** Defines "less than or equal" [[JaCoPConstraint]] between two [[Rand]].
      *
      * @param that a second parameter for "less than or equal" [[JaCoPConstraint]].
      * @return the defined [[JaCoPConstraint]].
      */
    def <=(that: Rand): JaCoPConstraint = {
      val c = new XlteqY(this, that)
      val crvc = new JaCoPConstraint(c)
      model.crvconstr += crvc
      crvc
    }

    /** Defines "less than or equal" [[JaCoPConstraint]] between [[Rand]] and integer constant.
      *
      * @param that a second parameter for "less than or equal" [[JaCoPConstraint]].
      * @return the equation [[JaCoPConstraint]].
      */
    def <=(that: BigInt): JaCoPConstraint = {
      require(that <= Int.MaxValue)
      val c = new XlteqC(this, that.toInt)
      val crvc = new JaCoPConstraint(c)
      model.crvconstr += crvc
      crvc
    }

    /** Defines "greater than" [[JaCoPConstraint]] between two [[Rand]].
      *
      * @param that a second parameter for "greater than" [[JaCoPConstraint]].
      * @return the defined [[JaCoPConstraint]].
      */
    def >(that: Rand): JaCoPConstraint = {
      val c = new XgtY(this, that)
      val crvc = new JaCoPConstraint(c)
      model.crvconstr += crvc
      crvc
    }

    /** Defines "greater than" [[JaCoPConstraint]] between [[Rand]] and integer constant.
      *
      * @param that a second parameter for "greater than" [[JaCoPConstraint]].
      * @return the equation [[JaCoPConstraint]].
      */
    def >(that: BigInt): JaCoPConstraint = {
      require(that <= Int.MaxValue)
      val c = new XgtC(this, that.toInt)
      val crvc = new JaCoPConstraint(c)
      model.crvconstr += crvc
      crvc
    }

    /** Defines "greater than or equal" [[JaCoPConstraint]] between two [[Rand]].
      *
      * @param that a second parameter for "greater than or equal" [[JaCoPConstraint]].
      * @return the defined [[JaCoPConstraint]].
      */
    def >=(that: Rand): JaCoPConstraint = {
      val c = new XgteqY(this, that)
      val crvc = new JaCoPConstraint(c)
      model.crvconstr += crvc
      crvc
    }

    /** Defines "greater than or equal" [[JaCoPConstraint]] between [[Rand]] and integer constant.
      *
      * @param that a second parameter for "greater than or equal" [[JaCoPConstraint]].
      * @return the equation [[JaCoPConstraint]].
      */
    def >=(that: BigInt): JaCoPConstraint = {
      require(that <= Int.MaxValue)
      val c = new XgteqC(this, that.toInt)
      val crvc = new JaCoPConstraint(c)
      model.crvconstr += crvc
      crvc
    }

    /** defines the add constraint between two [[Rand]] variables.
      * @param that a second parameter for the addition constraint
      * @return rand variable being the result of the addition constraint.
      */
    def #+(that: U): U = this.+(that)

    /** defines the add constraint between a [[Rand]] variable and a BigInt.
      * @param that a second parameter for the addition constraint
      * @return rand variable being the result of the addition constraint.
      */
    def #+(that: BigInt): U = this.+(that)

    /** defines the subtraction constraint between two [[Rand]] variables.
      * @param that a second parameter for the addition constraint
      * @return rand variable being the result of the addition constraint.
      */
    def #-(that: U): U = this.-(that)

    /** defines the subtraction constraint between a [[Rand]] variable and a BigInt.
      * @param that a second parameter for the addition constraint
      * @return rand variable being the result of the addition constraint.
      */
    def #-(that: BigInt): U = this.-(that)

    /** defines the multiplication constraint between two [[Rand]] variables.
      * @param that a second parameter for the addition constraint
      * @return rand variable being the result of the addition constraint.
      */
    def #*(that: U): U = this.*(that)

    /** defines the multiplication constraint between a [[Rand]] variable and a BigInt.
      * @param that a second parameter for the addition constraint
      * @return rand variable being the result of the addition constraint.
      */
    def #*(that: BigInt): U = this.*(that)

    /** defines the exponential constraint between two [[Rand]] variables.
      * @param that a second parameter for the addition constraint
      * @return rand variable being the result of the addition constraint.
      */

    /** defines the exponential constraint between a [[Rand]] variable and a BigInt.
      * @param that a second parameter for the addition constraint
      * @return rand variable being the result of the addition constraint.
      */
    def #^(that: BigInt): U = this.^(that)

    /** Defines inequality [[JaCoPConstraint]] between [[Rand]] and BigInt constant.
      *
      * @param that a second parameter for inequality [[JaCoPConstraint]].
      * @return the defined [[JaCoPConstraint]].
      */
    def #\=(that: U): JaCoPConstraint = this.\=(that)

    /** Defines inequality [[JaCoPConstraint]] between [[Rand]] and BigInt constant.
      *
      * @param that a second parameter for inequality [[JaCoPConstraint]].
      * @return the defined [[JaCoPConstraint]].
      */
    def #\=(that: BigInt): JaCoPConstraint = this.\=(that)

    /** Defines inequality [[JaCoPConstraint]] between [[Rand]] and BigInt constant.
      *
      * @param that a second parameter for inequality [[JaCoPConstraint]].
      * @return the defined [[JaCoPConstraint]].
      */
    def #=(that: U): JaCoPConstraint = this.==(that)

    /** Defines inequality [[JaCoPConstraint]] between [[Rand]] and BigInt constant.
      *
      * @param that a second parameter for inequality [[JaCoPConstraint]].
      * @return the defined [[JaCoPConstraint]].
      */
    def #=(that: BigInt): JaCoPConstraint = this.==(that)

    /** Defines "less than" [[JaCoPConstraint]] between two [[Rand]].
      *
      * @param that a second parameter for "less than" [[JaCoPConstraint]].
      * @return the defined [[JaCoPConstraint]].
      */
    def #<(that: U): JaCoPConstraint = this.<(that)

    /** Defines "less than" [[JaCoPConstraint]] between [[Rand]] and BigInt constant.
      *
      * @param that a second parameter for "less than" [[JaCoPConstraint]].
      * @return the equation [[JaCoPConstraint]].
      */
    def #<(that: BigInt): JaCoPConstraint = this.<(that)

    /** Defines "less than or equal" [[JaCoPConstraint]] between two [[Rand]].
      *
      * @param that a second parameter for "less than or equal" [[JaCoPConstraint]].
      * @return the defined [[JaCoPConstraint]].
      */
    def #<=(that: U): JaCoPConstraint = this.<=(that)

    /** Defines "less than or equal" [[JaCoPConstraint]] between [[Rand]] and BigInt constant.
      *
      * @param that a second parameter for "less than or equal" [[JaCoPConstraint]].
      * @return the equation [[JaCoPConstraint]].
      */
    def #<=(that: BigInt): JaCoPConstraint = this.<=(that)

    /** Defines "greater than" [[JaCoPConstraint]] between two [[Rand]].
      *
      * @param that a second parameter for "greater than or equal" [[JaCoPConstraint]].
      * @return the defined [[JaCoPConstraint]].
      */
    def #>(that: U): JaCoPConstraint = this.>(that)

    /** Defines "greater than" [[JaCoPConstraint]] between [[Rand]] and BigInt constant.
      *
      * @param that a second parameter for "greater than or equal" [[JaCoPConstraint]].
      * @return the equation [[JaCoPConstraint]].
      */
    def #>(that: BigInt): JaCoPConstraint = this.>(that)

    /** Defines "greater than or equal" [[JaCoPConstraint]] between two [[Rand]].
      *
      * @param that a second parameter for "greater than or equal" [[JaCoPConstraint]].
      * @return the defined [[JaCoPConstraint]].
      */
    def #>=(that: U): JaCoPConstraint = this.>=(that)

    /** Defines "greater than or equal" [[JaCoPConstraint]] between [[Rand]] and BigInt constant.
      *
      * @param that a second parameter for "greater than or equal" [[JaCoPConstraint]].
      * @return the equation [[JaCoPConstraint]].
      */
    def #>=(that: BigInt): JaCoPConstraint = this.>=(that)

    /** Defines [[JaCoPConstraint]] on inclusion of a [[Rand]] variable value in a set.
      *
      * @param that set that this variable's value must be included.
      * @return the equation [[JaCoPConstraint]].
      */
    def in(that: SetVar): JaCoPConstraint = {
      if (min == max) {
        val c = new EinA(min, that)
        val crvc = new JaCoPConstraint(c)
        model.crvconstr += crvc
        crvc
      } else {
        val c = new XinA(this, that)
        val crvc = new JaCoPConstraint(c)
        model.crvconstr += crvc
        crvc
      }
    }

    /** Defines [[JaCoPConstraint]] on inclusion of a [[Rand]] variable value in a set.
      *
      * @param that set that this variable's value must be included.
      * @return the equation [[JaCoPConstraint]].
      */
    def inside(that: SetVar): JaCoPConstraint = {
      this.in(that)
    }

    /**
      * Create a distribution constraint for the current value
      * @param groups the [[WeightedRange]] or [[WeightedValue]] to assign to the current variable
      * @return the [[DistConstraint]]
      */
    def dist(groups: Weight*): DistConstraint = {
      val c = new DistConstraint(this, groups.toList)
      model.distConst += c
      c
    }
  }

  private[crv] class Randc(min: Int, max: Int)(implicit model: Model)
    extends Rand(randName(10 ,model.seed), min, max) with chiselverify.crv.Randc {
    model.randcVars += this

    private val rand = new Random(model.seed)
    private var currentValue: Int = (math.abs(rand.nextInt()) % (max - min)) + min

    /** Returns the current value of the variable
      * @return return the current value of the variable
      */
    override def value(): Int = currentValue.toInt

    /** Gets the next value of the random variable
      * @return return the next value of the variable
      */
    override def next(): Int = {
      currentValue = if (currentValue == max) min else currentValue + 1
      currentValue
    }

    /** Set the value of the variable
      * @param that the value to be set
      */
    override def setVar(that: Int): Unit = currentValue = that
  }

  abstract class RandType
  case object Normal extends RandType
  case object Cyclic extends RandType

  /**
    * Allows for the declaration of either type of RandomVar.
    * @param min the minimum value that the random variable can take.
    * @param max the maximum value that the random variable can take.
    * @param randType Either Normal or Cyclic, the type of the random variable.
    * @param model the model used to store the random data.
    * @return a random variable of the correct type
    */
  def rand(min: Int, max: Int, randType: RandType = Normal)(implicit model: Model) : Rand = randType match {
    case Normal =>
      val rand = new Random(model.seed)
      val randLength = (rand.nextInt() % 30) + 10
      new Rand(randName(randLength, model.seed), min, max)

    case Cyclic => new Randc(min, max)
  }
}


package examples.heappriorityqueue

import chisel3._
import chisel3.util._

/**
  * contains relevant bundle types and port types for the heap-based priority queue
  */
object Interfaces {

  class Event(implicit parameters: PriorityQueueParameters) extends Bundle {
    import parameters._
    val cycle = UInt(cycleWidth.W)
    val superCycle = UInt(superCycleWidth.W)

    def <(that: Event): Bool = superCycle < that.superCycle || (superCycle === that.superCycle && cycle < that.cycle)

    def ===(that: Event): Bool = superCycle === that.superCycle && cycle === that.cycle
  }

  class TaggedEvent(implicit parameters: PriorityQueueParameters) extends Bundle {
    import parameters._
    val event = new Event
    val id = UInt(referenceIdWidth.W)
  }

  class rdPort[T <: Data](addrWid: Int, dType: T) extends Bundle { // as seen from reader side
    val address = Output(UInt(addrWid.W))
    val data = Input(dType)
  }

  class wrPort[T <: Data](addrWid: Int, maskWid: Int, dType: T) extends Bundle { // as seen from writer side
    val address = Output(UInt(addrWid.W))
    val mask = Output(UInt(maskWid.W))
    val data = Output(dType)
    val write = Output(Bool())
  }

  class searchPort(implicit parameters: PriorityQueueParameters) extends Bundle { // as seen from requester side
    import parameters._
    val refID = Output(UInt(referenceIdWidth.W))
    val heapSize = Output(UInt(log2Ceil(size + 1).W))
    val res = Input(UInt(log2Ceil(size).W))
    val search = Output(Bool())
    val error = Input(Bool())
    val done = Input(Bool())
  }
}

def >(that: Event): Bool = superCycle > that.superCycle || (superCycle === that.superCycle) && cycle > that.cycle


package examples.heappriorityqueue

import chisel3._
import chisel3.util._

import examples.heappriorityqueue.Interfaces._
import examples.heappriorityqueue.modules.{QueueControl, linearSearchMem}

/**
  * Component implementing a priority queue, where the minimum value gets to the head of the queue
  *  - The sorting is based on heap sort
  *  - inserted priorities are made up of 3 fields:
  *    - cyclic priority which is most important
  *    - normal priority which decides in the case of equal cyclic priorities
  *    - a user generated reference ID given at insertion which is used to remove elements from the queue
  *  - the component needs to be provided with the following:
  *    - a synchronous memory of appropriate size
  *      - the memory has to be initialized to all 1s
  *      - thus the reference ID with all 1s is reserved for empty cells
  *    - a component which is able to search through the reference ID fields and return the index where a given reference ID is present
  *      - the queue waits until the search result is given
  *  - the head element is kept locally in a FF for symmetry and access speed reasons
  *  - the head element is furthermore presented at a dedicated port
  *
  * @param size    the maximum size of the heap
  * @param order the number of children per node in the tree. Must be power of 2
  * @param nWid    width of the normal priority value
  * @param cWid    width of the cyclic priority value
  * @param rWid    width of the reference ID tags
  */
class PriorityQueue(size: Int, order: Int, superCycleRes: Int, cyclesPerSuperCycle: Int, exposeState: Boolean = false) extends Module {
  require(isPow2(order), "The number of children per node needs to be a power of 2!")

  val superCycleWidth = superCycleRes
  val cycleWidth = log2Ceil(cyclesPerSuperCycle)
  val referenceIdWidth = log2Ceil(size+1)
  implicit val parameters = PriorityQueueParameters(size, order, superCycleWidth, cycleWidth, referenceIdWidth)

  val io = IO(new Bundle {
    // Interface for signaling head element to user.
    // I.e. the element with the lowest priority
    val head = new Bundle {
      val valid = Output(Bool())
      val none = Output(Bool())
      val prio = Output(new Event)
      val refID = Output(UInt(referenceIdWidth.W))
    }

    // Interface for element insertion/removal
    // Timing:
    // User must maintain input asserted until done is asserted.
    // User must deassert input when done is asserted (unless a new cmd is made).
    // User must ensure that reference ID tags are unique.
    val cmd = new Bundle {
      // inputs
      val valid = Input(Bool())
      val op = Input(Bool()) // 0=Remove, 1=Insert
      val prio = Input(new Event)
      val refID = Input(UInt(referenceIdWidth.W))
      // outputs
      val done = Output(Bool())
      val result = Output(Bool()) // 0=Success, 1=Failure
      val rm_prio = Output(new Event)
    }

    val state = if (exposeState) Some(Output(UInt())) else None
  })

  val mem = Module(new linearSearchMem(size - 1))
  val queue = Module(new QueueControl)

  mem.srch <> queue.io.srch
  mem.rd <> queue.io.rdPort
  mem.wr <> queue.io.wrPort
  io.head <> queue.io.head
  io.cmd <> queue.io.cmd

  io.cmd.done := mem.srch.done && queue.io.cmd.done

  if (exposeState) io.state.get := queue.io.state
}

case class PriorityQueueParameters(size: Int, order: Int, superCycleWidth: Int, cycleWidth: Int, referenceIdWidth: Int)


package examples.heappriorityqueue

import chiseltest._
import org.scalatest.freespec.AnyFreeSpec
import org.scalatest.matchers.should.Matchers

import examples.heappriorityqueue.Helpers._
import examples.heappriorityqueue.modules.MinFinder

/**
  * contains a randomized test for the MinFinder module
  */
class MinFinderTest extends AnyFreeSpec with ChiselScalatestTester with Matchers {
  def calculateOut(values: Seq[Seq[Int]]): Int = {
    val cyclic = values.map(_.head)
    val cyclicMins = cyclic.zipWithIndex.filter(_._1 == cyclic.min).map(_._2)
    if (cyclicMins.length == 1) {
      cyclicMins.head
    } else {
      val normals = values.map(_ (1))

      candidates.indexOf(candidates.min)
    }
  }

  val n = 8
  implicit val parameters = PriorityQueueParameters(32, 4, 4, 8, 5)

  "MinFinder should identify minimum value with the lowest index" in {
    test(new MinFinder(n)) { dut =>
      import parameters._
      setWidths(superCycleWidth, cycleWidth, referenceIdWidth)

      for (_ <- 0 until 1000) {
        val values = pokePrioAndIDVec(dut.io.values)
        dut.io.index.peek().litValue should equal (calculateOut(values))
        peekPrioAndId(dut.io.res) should equal (values(calculateOut(values)))
      }
    }
  }
}

val candidates = Seq.tabulate(values.length)(i => if (cyclicMins.contains(i)) normals(i) else Int.MaxValue)