package chiselv

import chiseltest._
import chiseltest.experimental._
import com.carlosedp.riscvassembler.RISCVAssembler
import org.scalatest._

import flatspec._
import matchers._

// Extend the Control module to add the observer for sub-module signals
class CPUSingleCycleInstWrapper(memoryFile: String) extends SOC(
      cpuFrequency          = 25000000,
      entryPoint            = 0,
      bitWidth              = 32,
      instructionMemorySize = 1 * 1024,
      dataMemorySize        = 1 * 1024,
      memoryFile            = memoryFile,
      numGPIO               = 0,
    ) {
  val registers    = expose(core.registerBank.regs)
  val pc           = expose(core.PC.pc)
  val memWriteAddr = expose(core.memoryIOManager.io.MemoryIOPort.writeAddr)
  val memWriteData = expose(core.memoryIOManager.io.MemoryIOPort.writeData)
  val memReadAddr  = expose(core.memoryIOManager.io.MemoryIOPort.readAddr)
  val memReadData  = expose(core.memoryIOManager.io.MemoryIOPort.readData)
}

class CPUSingleCycleInstructionSpec
    extends AnyFlatSpec
    with ChiselScalatestTester
    with BeforeAndAfterEach
    with BeforeAndAfterAll
    with should.Matchers {
  val memReadLatency  = 1
  val memWriteLatency = 1
  var memoryfile: os.Path = _
  val tmpdir = os.pwd / "tmphex"

  override def beforeAll(): Unit =
    os.makeDir.all(tmpdir)
  override def afterAll(): Unit =
    scala.util.Try(os.remove(tmpdir))
  override def beforeEach(): Unit =
    memoryfile = tmpdir / (scala.util.Random.alphanumeric.filter(_.isLetter).take(15).mkString + ".hex")
  override def afterEach(): Unit =
    os.remove.all(memoryfile)

  def defaultDut(prog: String) = {
    // Generate the hex file from asm source
    val hex = RISCVAssembler.fromString(prog)
    os.write(memoryfile, hex)
    test(new CPUSingleCycleInstWrapper(memoryfile.relativeTo(os.pwd).toString))
      .withAnnotations(
        Seq(
          WriteVcdAnnotation
        )
      )
  }

  it should "validate ADD/ADDI instructions" in {
    val prog = """
    addi x1, x1, 1
    addi x2, x2, 1
    add x3, x1, x2
    """
    defaultDut(prog) { c =>
      c.clock.setTimeout(0)
      c.registers(1).peekInt() should be(0)
      c.registers(2).peekInt() should be(0)
      c.registers(3).peekInt() should be(0)
      c.clock.step(1)
      c.registers(1).peekInt() should be(1)
      c.clock.step(1)
      c.registers(2).peekInt() should be(1)
      c.clock.step(1)
      c.registers(3).peekInt() should be(2)
    }
  }

  it should "validate Branch instructions" in {
    val prog = """
    addi x1, x0, 4
    addi x2, x0, 4
    addi x3, x0, 2
    beq x1, x2, +8
    jal x0, 0
    bne x2, x3, +8
    jal x0, 0
    blt x3, x2, +8
    jal x0, 0
    bge x2, x3, +8
    jal x0, 0
     """
    defaultDut(prog) { c =>
      c.clock.setTimeout(0)
      c.registers(1).peekInt() should be(0x0)
      c.registers(2).peekInt() should be(0x0)
      c.registers(3).peekInt() should be(0x0)
      c.clock.step(1)
      c.registers(1).peekInt() should be(4)
      c.clock.step(1)
      c.registers(2).peekInt() should be(4)
      c.clock.step(1)
      c.registers(3).peekInt() should be(2)
      c.pc.peekInt() should be(0x0c)
      c.clock.step(1)
      c.pc.peekInt() should be(0x14)
      c.clock.step(1)
      c.pc.peekInt() should be(0x1c)
      c.clock.step(1)
      c.pc.peekInt() should be(0x24)
      c.clock.step(1)
      c.pc.peekInt() should be(0x2c)
    }
  }

  it should "validate JAL/JALR instructions" in {
    val prog = """

      c.pc.peekInt() should be(8)
      c.registers(1).peekInt() should be(4)
      c.clock.step(1) // jalr
      c.pc.peekInt() should be(2048)
      c.registers(2).peekInt() should be(0xc)
    }
  }

  it should "validate LUI instruction" in {
    val prog = """
    lui x2, 0xc0000
    """
    defaultDut(prog) { c =>
      c.clock.setTimeout(0)
      c.registers(2).peekInt() should be(0x00000000L)
      c.clock.step(1)
      c.registers(2).peekInt() should be(0xc0000000L)
    }
  }

  it should "validate AUIPC instruction" in {
    val prog = """
    auipc x2, 4096
    auipc x3, 4096
    """
    defaultDut(prog) { c =>
      c.clock.setTimeout(0)
      c.registers(2).peekInt() should be(0x00000000L)
      c.clock.step(1)
      c.registers(2).peekInt() should be(0x01000000L)
      c.clock.step(1)
      c.registers(3).peekInt() should be(0x01000004L)
    }
  }

  it should "validate SW instruction" in {
    val prog = """
      lui x1, 0x80000
      addi x1, x1, 20
      addi x2, x0, 291
      sw x2, 0(x1)
      lw x3, 0(x1)
    """
    defaultDut(prog) { c =>
      c.registers(1).peekInt() should be(0)
      c.clock.step(1) // lui
      c.registers(1).peekInt() should be(0x80000000L)
      c.clock.step(1) // addi
      c.registers(1).peekInt() should be(0x80000014L)
      c.clock.step(1) // addi
      c.registers(2).peekInt() should be(0x123)
      // Check memory address 0x14 with offset
      c.memWriteAddr.peekInt() should be(0x80000000L + 0x14)
      c.memWriteData.peekInt() should be(0x123)
      c.clock.step(1 + memWriteLatency) // sw
      // Check memory address 0x14 with offset
      c.memReadAddr.peekInt() should be(0x80000000L + 0x14)
      c.memReadData.peekInt() should be(0x123)
      c.clock.step(2 + memReadLatency) // lw
      c.registers(3).peekInt() should be(0x123)
    }
  }

  it should "validate SH instruction" in {
    val prog = """
    lui x1, 0x80000
    lui x2, 0x12345
    addi x2, x2, 1656
    addi x3, x3, -1
    sw x3, 48(x1)  // Memory offset 48 (0x30) should be 0xffffffff
    sh x2, 52(x1)  // Memory offset 52 (0x34) should be 0x00005678
    sh x2, 48(x1)  // Memory offset 48 (0x30) should be 0xffff5678
    sh x2, 54(x1)  // Memory offset 52 (0x34) should be 0x56785678
    lw x4, 48(x1)  // x4 should be 0xffff5678
    lw x5, 52(x1)  // x5 should be 0x56785678
    """
    defaultDut(prog) { c =>
      c.registers(1).peekInt() should be(0)
      c.clock.step(1) // lui
      c.registers(1).peekInt() should be(0x80000000L)
      c.clock.step(1) // lui
      c.registers(2).peekInt() should be(0x12345000L)
      c.clock.step(1) // addi
      c.registers(2).peekInt() should be(0x12345678L)
      c.clock.step(1) // addi
      c.registers(3).peekInt() should be(0xffffffffL)
      // Check memory address offset 0x30
      c.memWriteAddr.peekInt() should be(0x80000000L + 0x30)
      c.memWriteData.peekInt() should be(0xffffffffL)
      c.clock.step(1 + memWriteLatency) // sw
      // Check memory address offset 0x34
      c.memWriteAddr.peekInt() should be(0x80000000L + 0x34)
      c.memWriteData.peekInt() should be(0x5678L)
      c.clock.step(1 + memWriteLatency) // sh
      // Check memory address 0x30
      c.memWriteAddr.peekInt() should be(0x80000000L + 0x30)
      c.memWriteData.peekInt() should be(0x5678L)
      c.clock.step(1 + memWriteLatency) // sh
      // Check memory address offset 0x34
      c.memWriteAddr.peekInt() should be(0x80000000L + 0x36)
      c.memWriteData.peekInt() should be(0x5678L)
      c.clock.step(1 + memWriteLatency) // sh
      // Check memory address 0x30
      c.clock.step(1) // lw - stall
      c.memReadAddr.peekInt() should be(0x80000000L + 0x30)
      c.memReadData.peekInt() should be(0xffff5678L)
      c.clock.step(1) // lw
      c.clock.step(1) // lw - stall
      c.registers(4).peekInt() should be(0xffff5678L)
      // Check memory address 0x34
      c.memReadAddr.peekInt() should be(0x80000000L + 0x34)
      c.memReadData.peekInt() should be(0x56785678L)
      c.clock.step(1) // lw
      c.registers(5).peekInt() should be(0x56785678L)
      c.clock.step(10) // padding
    }
  }

  it should "validate SB instruction" in {
    val prog = """
    lui x1, 0x80000
    lui x2, 0x12345
    addi x2, x2, 1656
    addi x3, x3, -1
    sw x3, 48(x1)  // Memory offset 48 (0x30) should be 0xffffffff
    sb x2, 52(x1)  // Memory offset 52 (0x34) should be 0x00000078
    sb x2, 48(x1)  // Memory offset 48 (0x30) should be 0xffffff78
    sb x2, 54(x1)  // Memory offset 52 (0x34) should be 0x00780078
    sb x2, 51(x1)  // Memory offset 51 (0x33) should be 0x78ffff78
    lw x4, 48(x1)  // x4 should be 0x78ffff78
    lw x5, 52(x1)  // x5 should be 0x00780078
    """
    defaultDut(prog) { c =>
      c.registers(1).peekInt() should be(0)
      c.clock.step(1) // lui
      c.registers(1).peekInt() should be(0x80000000L)
      c.clock.step(1) // lui
      c.registers(2).peekInt() should be(0x12345000L)
      c.clock.step(1) // addi
      c.registers(2).peekInt() should be(0x12345678L)
      c.clock.step(1) // addi
      c.registers(3).peekInt() should be(0xffffffffL)
      // Check memory address offset 0x30
      c.memWriteAddr.peekInt() should be(0x80000000L + 0x30)
      c.memWriteData.peekInt() should be(0xffffffffL)
      c.clock.step(1 + memWriteLatency) // sw
      // Check memory address offset 0x34
      c.memWriteAddr.peekInt() should be(0x80000000L + 0x34)
      c.memWriteData.peekInt() should be(0x78L)
      c.clock.step(1 + memWriteLatency) // sb
      // Check memory address 0x30
      c.memWriteAddr.peekInt() should be(0x80000000L + 0x30)
      c.memWriteData.peekInt() should be(0x78L)
      c.clock.step(1 + memWriteLatency) // sb
      // Check memory address offset 0x34
      c.memWriteAddr.peekInt() should be(0x80000000L + 0x36)
      c.memWriteData.peekInt() should be(0x78L)
      c.clock.step(1 + memWriteLatency) // sb
      // Check memory address offset 0x33
      c.memWriteAddr.peekInt() should be(0x80000000L + 0x33)
      c.memWriteData.peekInt() should be(0x78L)
      c.clock.step(1 + memWriteLatency) // sb
      // Check memory address 0x30
      c.memReadAddr.peekInt() should be(0x80000000L + 0x30)
      c.clock.step(1) // lw
      c.memReadData.peekInt() should be(0x78ffff78L)
      c.clock.step(1) // lw
      c.registers(4).peekInt() should be(0x78ffff78L)
      // Check memory address 0x34
      c.memReadAddr.peekInt() should be(0x80000000L + 0x34)
      c.clock.step(1) // lw
      c.memReadData.peekInt() should be(0x00780078L)
      c.clock.step(1) // lw
      c.registers(5).peekInt() should be(0x00780078L)
      c.clock.step(10) // padding
    }
  }

  it should "validate LW instruction" in {
    val prog = """
    lui x1, 0xf0f0f
    addi x1, x1, 240
    lui x2, 0x80000
    sw x1, 0(x2)
    lw x3, 0(x2)
    """
    defaultDut(prog) { c =>
      c.registers(1).peekInt() should be(0)
      c.clock.step(1) // lui
      c.registers(1).peekInt() should be(0xf0f0f000L)
      c.clock.step(1) // addi
      c.registers(1).peekInt() should be(0xf0f0f0f0L)
      c.clock.step(1) // lui
      c.registers(2).peekInt() should be(0x80000000L)
      // Check memory write at address 0x80000000L
      c.memWriteAddr.peekInt() should be(0x80000000L)
      c.memWriteData.peekInt() should be(0xf0f0f0f0L)
      c.clock.step(1 + memWriteLatency) // sw
      // Check memory read at address 0x20 (32)
      c.memReadAddr.peekInt() should be(0x80000000L)
      c.memReadData.peekInt() should be(0xf0f0f0f0L)
      c.clock.step(1 + memReadLatency) // lw
      // Check loaded data
      c.registers(3).peekInt() should be(0xf0f0f0f0L)
      c.clock.step(5) // Paddding
    }
  }

  it should "validate LH instruction" in {
    val prog = """
      lui x1, 0xffff1
      addi x1, x1, 564
      lui x2, 0x80000
      sw x1, 0(x2)
      lh x3, 0(x2)
      lh x4, 2(x2)
      """
    defaultDut(prog) { c =>
      c.registers(1).peekInt() should be(0)
      c.clock.step(1)
      c.registers(1).peekInt() should be(0xffff1000L)
      c.clock.step(1)
      c.registers(1).peekInt() should be(0xffff1234L)
      c.clock.step(1)
      c.registers(2).peekInt() should be(0x80000000L)
      // Check memory write at address 0x80000000L
      c.memWriteAddr.peekInt() should be(0x80000000L)
      c.memWriteData.peekInt() should be(0xffff1234L)
      c.clock.step(1 + memWriteLatency) // sw
      // Check memory read at address 0x80000000L
      c.memReadAddr.peekInt() should be(0x80000000L)
      c.memReadData.peekInt() should be(0x00001234L)
      c.clock.step(1 + memReadLatency) // lh
      // Check loaded data
      c.registers(3).peekInt() should be(0x00001234L)
      // Check memory read at address 0x80000002L
      c.memReadAddr.peekInt() should be(0x80000000L + 0x2)
      c.memReadData.peekInt() should be(0x0000ffffL)
      c.clock.step(1 + memReadLatency) // lh
      c.registers(4).peekInt() should be(0xffffffffL)
      c.clock.step(5) // Paddding
    }
  }

  it should "validate LHU instruction" in {
    val prog = """
    lui x1, 0xffff1
    addi x1, x1, 564
    lui x2, 0x80000
    sw x1, 0(x2)
    lhu x3, 0(x2)
    lhu x4, 2(x2)
    """
    defaultDut(prog) { c =>
      c.registers(1).peekInt() should be(0)
      c.clock.step(1)
      c.registers(1).peekInt() should be(0xffff1000L)
      c.clock.step(1)
      c.registers(1).peekInt() should be(0xffff1234L)
      c.clock.step(1)
      c.registers(2).peekInt() should be(0x80000000L)
      // Check memory write at address 0x80000000L
      c.memWriteAddr.peekInt() should be(0x80000000L)
      c.memWriteData.peekInt() should be(0xffff1234L)
      c.clock.step(1 + memWriteLatency) // sw
      // Check memory read at address 0x80000000L
      c.memReadAddr.peekInt() should be(0x80000000L)
      c.memReadData.peekInt() should be(0x00001234L)
      c.clock.step(1 + memReadLatency) // lh
      // Check loaded data
      c.registers(3).peekInt() should be(0x00001234L)
      // Check memory read at address 0x80000002L
      c.memReadAddr.peekInt() should be(0x80000000L + 0x2)
      c.memReadData.peekInt() should be(0x0000ffffL)
      c.clock.step(1 + memReadLatency) // lh
      c.registers(4).peekInt() should be(0x0000ffffL)
      c.clock.step(5) // Paddding
    }
  }

  it should "validate LB instruction" in {
    val prog = """
    lui x1, 0xabcde
    addi x1, x1, 0x123
    lui x2, 0x80000
    sw x1, 0(x2)
    lb x3, 0(x2)
    lb x4, 1(x2)
    lb x5, 2(x2)
    lb x6, 3(x2)
    """
    defaultDut(prog) { c =>
      c.registers(1).peekInt() should be(0)
      c.clock.step(1)
      c.registers(1).peekInt() should be(0xabcde000L)
      c.clock.step(1)
      c.registers(1).peekInt() should be(0xabcde123L)
      c.clock.step(1)
      c.registers(2).peekInt() should be(0x80000000L)
      // Check memory write at address 0x80000000L
      c.memWriteAddr.peekInt() should be(0x80000000L)
      c.memWriteData.peekInt() should be(0xabcde123L)
      c.clock.step(1 + memWriteLatency) // sw
      // Check memory read at address 0x80000000L
      c.memReadAddr.peekInt() should be(0x80000000L)
      c.memReadData.peekInt() should be(0x23L)
      c.clock.step(1 + memReadLatency) // lb (offset 0)
      c.registers(3).peekInt() should be(0x00000023L)
      // Check memory read at address 0x80000001L
      c.memReadAddr.peekInt() should be(0x80000001L)
      c.memReadData.peekInt() should be(0xe1L)
      c.clock.step(1 + memReadLatency) // lb (offset 1)
      c.registers(4).peekInt() should be(0xffffffe1L)
      // Check memory read at address 0x80000002L
      c.memReadAddr.peekInt() should be(0x80000002L)
      c.memReadData.peekInt() should be(0xcdL)
      c.clock.step(1 + memReadLatency) // lb (offset 2)
      c.registers(5).peekInt() should be(0xffffffcdL)
      // Check memory read at address 0x80000003L
      c.memReadAddr.peekInt() should be(0x80000003L)
      c.memReadData.peekInt() should be(0xabL)
      c.clock.step(1 + memReadLatency) // lb (offset 3)
      c.registers(6).peekInt() should be(0xffffffabL)
      // Check loaded data
      c.clock.step(5) // Paddding
    }
  }

  it should "validate LBU instruction" in {
    val prog = """
    lui x1, 0xabcde
    addi x1, x1, 0x123
    lui x2, 0x80000
    sw x1, 0(x2)
    lbu x3, 0(x2)
    lbu x4, 1(x2)
    lbu x5, 2(x2)
    lbu x6, 3(x2)
    """
    defaultDut(prog) { c =>
      c.registers(1).peekInt() should be(0)
      c.clock.step(1)
      c.registers(1).peekInt() should be(0xabcde000L)
      c.clock.step(1)
      c.registers(1).peekInt() should be(0xabcde123L)
      c.clock.step(1)
      c.registers(2).peekInt() should be(0x80000000L)
      // Check memory write at address 0x80000000L
      c.memWriteAddr.peekInt() should be(0x80000000L)
      c.memWriteData.peekInt() should be(0xabcde123L)
      c.clock.step(1 + memWriteLatency) // sw
      // Check memory read at address 0x80000000L
      c.memReadAddr.peekInt() should be(0x80000000L)
      c.memReadData.peekInt() should be(0x23L)
      c.clock.step(1 + memReadLatency) // lb (offset 0)
      c.registers(3).peekInt() should be(0x00000023L)
      // Check memory read at address 0x80000001L
      c.memReadAddr.peekInt() should be(0x80000001L)
      c.memReadData.peekInt() should be(0xe1L)
      c.clock.step(1 + memReadLatency) // lb (offset 1)
      c.registers(4).peekInt() should be(0x000000e1L)
      // Check memory read at address 0x80000002L
      c.memReadAddr.peekInt() should be(0x80000002L)
      c.memReadData.peekInt() should be(0xcdL)
      c.clock.step(1 + memReadLatency) // lb (offset 2)
      c.registers(5).peekInt() should be(0x000000cdL)
      // Check memory read at address 0x80000003L
      c.memReadAddr.peekInt() should be(0x80000003L)
      c.memReadData.peekInt() should be(0xabL)
      c.clock.step(1 + memReadLatency) // lb (offset 3)
      c.registers(6).peekInt() should be(0x000000abL)
      // Check loaded data
      c.clock.step(5) // Paddding
    }
  }
}

      jal x1, +8
      nop
      jalr x2, x1, +2044
      """
    defaultDut(prog) { c =>


package chiselv

import chisel3._
import chiseltest._
import com.carlosedp.riscvassembler.RISCVAssembler
import org.scalatest._

import Instruction._
import flatspec._
import matchers._

class DecoderSpec extends AnyFlatSpec with ChiselScalatestTester with should.Matchers {
  behavior of "Decoder - Aritmetic"

  it should "Decode an ADD instruction (type R)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("add x1, x2, x3"))
      c.clock.step()
      validateResult(c, ADD, 1, 2, 3, 0, true, false)
    }
  }
  it should "Decode an ADDI instruction (type I)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("addi x1, x2, 5"))
      c.clock.step()
      validateResult(c, ADDI, 1, 2, 0, 5, true, false, true)
    }
  }
  it should "Decode an SUB instruction (type R)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("sub x1, x2, x3"))
      c.clock.step()
      validateResult(c, SUB, 1, 2, 3, 0, true, false)
    }
  }
  it should "Decode an LUI instruction (type U)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("lui x23, 0x12345"))
      c.clock.step()
      validateResult(c, LUI, 23, 0, 0, 305418240, false, false, true)
    }
  }
  it should "Decode an AUIPC instruction (type U)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("auipc x23, 0x12345"))
      c.clock.step()
      validateResult(c, AUIPC, 23, 0, 0, 305418240, false, false, true)
    }
  }

  behavior of "Decoder - Shifts"

  it should "Decode an SLL instruction (type R)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("sll x1, x2, x3"))
      c.clock.step()
      validateResult(c, SLL, 1, 2, 3, 0, true, false)
    }
  }
  it should "Decode an SLLI instruction (type I)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("slli x1, x2, 5"))
      c.clock.step()
      validateResult(c, SLLI, 1, 2, 0, 5, true, false, true)
    }
  }
  it should "Decode an SRL instruction (type R)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("srl x1, x2, x3"))
      c.clock.step()
      validateResult(c, SRL, 1, 2, 3, 0, true, false)
    }
  }
  it should "Decode an SRLI instruction (type I)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("srli x1, x2, 5"))
      c.clock.step()
      validateResult(c, SRLI, 1, 2, 0, 5, true, false, true)
    }
  }
  it should "Decode an SRA instruction (type R)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("sra x1, x2, x3"))
      c.clock.step()
      validateResult(c, SRA, 1, 2, 3, 0, true, false)
    }
  }
  it should "Decode an SRAI instruction (type I)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("srai x1, x2, 1"))
      c.clock.step()
      validateResult(c, SRAI, 1, 2, 0, 1025, true, false, true)
    }
  }

  behavior of "Decoder - Logical"

  it should "Decode an AND instruction (type R)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("and x1, x2, x3"))
      c.clock.step()
      validateResult(c, AND, 1, 2, 3, 0, true, false)
    }
  }
  it should "Decode an ANDI instruction (type I)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("andi x7, x24, -1"))
      c.clock.step()
      validateResult(c, ANDI, 7, 24, 0, -1, true, false, true)
    }
  }
  it should "Decode an OR instruction (type R)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("or x1, x2, x3"))
      c.clock.step()
      validateResult(c, OR, 1, 2, 3, 0, true, false)
    }
  }
  it should "Decode an ORI instruction (type I)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("ori x7, x24, -1"))
      c.clock.step()
      validateResult(c, ORI, 7, 24, 0, -1, true, false, true)
    }
  }
  it should "Decode an XOR instruction (type R)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("xor x1, x2, x3"))
      c.clock.step()
      validateResult(c, XOR, 1, 2, 3, 0, true, false)
    }
  }
  it should "Decode an XORI instruction (type I)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("xori x7, x24, -1"))
      c.clock.step()
      validateResult(c, XORI, 7, 24, 0, -1, true, false, true)
    }
  }

  behavior of "Decoder - Compare"

  it should "Decode an SLT instruction (type R)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("slt x1, x2, x3"))
      c.clock.step()
      validateResult(c, SLT, 1, 2, 3, 0, true, false)
    }
  }
  it should "Decode an SLTI instruction (type I)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("slti x7, x24, -1"))
      c.clock.step()
      validateResult(c, SLTI, 7, 24, 0, -1, true, false, true)
    }
  }
  it should "Decode an SLTU instruction (type R)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("sltu x1, x2, x3"))
      c.clock.step()
      validateResult(c, SLTU, 1, 2, 3, 0, true, false)
    }
  }
  it should "Decode an SLTIU instruction (type I)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("sltiu x7, x24, -1"))
      c.clock.step()
      validateResult(c, SLTIU, 7, 24, 0, -1, true, false, true)
    }
  }

  behavior of "Decoder - Branches"

  it should "Decode an BEQ instruction (type B)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("beq x21, x10, -1366"))
      c.clock.step()
      validateResult(c, BEQ, 0, 21, 10, -1366, false, true, true)
    }
  }
  it should "Decode an BNE instruction (type B)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("bne x21, x10, -1366"))
      c.clock.step()
      validateResult(c, BNE, 0, 21, 10, -1366, false, true, true)
    }
  }
  it should "Decode an BLT instruction (type B)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("blt x21, x10, -1366"))
      c.clock.step()
      validateResult(c, BLT, 0, 21, 10, -1366, false, true, true)
    }
  }
  it should "Decode an BGE instruction (type B)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("bge x21, x10, -1366"))
      c.clock.step()
      validateResult(c, BGE, 0, 21, 10, -1366, false, true, true)
    }
  }
  it should "Decode an BLTU instruction (type B)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("bltu x21, x10, -1366"))
      c.clock.step()
      validateResult(c, BLTU, 0, 21, 10, -1366, false, true, true)
    }
  }
  it should "Decode an BGEU instruction (type B)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("bgeu x21, x10, -1366"))
      c.clock.step()
      validateResult(c, BGEU, 0, 21, 10, -1366, false, true, true)
    }
  }

  behavior of "Decoder - Jumps"

  it should "Decode an JAL instruction (type J)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("jal x21, -699052"))
      c.clock.step()
      validateResult(c, JAL, 21, 0, 0, -699052, false, false, true, true)
    }
  }
  it should "Decode an JALR instruction (type I)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("jalr x21, x1, 1234"))
      c.clock.step()
      validateResult(c, JALR, 21, 1, 0, 1234, false, false, true, true)
    }
  }

  behavior of "Decoder - Loads/Stores"

  it should "Decode an LB instruction (type I)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("lb x1, 16(x2)"))
      c.clock.step()
      validateResult(c, LB, 1, 2, 0, 16, false, false, true, false, true, false)
    }
  }
  it should "Decode an LH instruction (type I)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("lh x1, 16(x2)"))
      c.clock.step()
      validateResult(c, LH, 1, 2, 0, 16, false, false, true, false, true, false)
    }
  }
  it should "Decode an LBU instruction (type I)" in {

  }
  it should "Decode an LHU instruction (type I)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("lhu x1, 16(x2)"))
      c.clock.step()
      validateResult(c, LHU, 1, 2, 0, 16, false, false, true, false, true, false)
    }
  }
  it should "Decode an LW instruction (type I)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("lw x1, 16(x2)"))
      c.clock.step()
      validateResult(c, LW, 1, 2, 0, 16, false, false, true, false, true, false)
    }
  }
  it should "Decode an SB instruction (type S)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("sb x10, -81(x21)"))
      c.clock.step()
      validateResult(c, SB, 0, 21, 10, -81, false, false, true, false, false, true)
    }
  }
  it should "Decode an SH instruction (type S)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("sh x10, -81(x21)"))
      c.clock.step()
      validateResult(c, SH, 0, 21, 10, -81, false, false, true, false, false, true)
    }
  }
  it should "Decode an SW instruction (type S)" in {
    test(new Decoder) { c =>
      c.io.op.poke(makeBin("sw x10, -81(x21)"))
      c.clock.step()
      validateResult(c, SW, 0, 21, 10, -81, false, false, true, false, false, true)
    }
  }

  // --------------------- Test Helpers ---------------------

  def makeBin(
      in: String
    ): UInt = {
    val bin = RISCVAssembler.binOutput(in)
    ("b" + bin).U
  }

  def validateResult(
      c:       Decoder,
      inst:    Instruction.Type,
      rd:      Int,
      rs1:     Int,
      rs2:     Int,
      imm:     Int,
      toALU:   Boolean = false,
      branch:  Boolean = false,
      use_imm: Boolean = false,
      jump:    Boolean = false,
      load:    Boolean = false,
      store:   Boolean = false,
    ) = {
    c.io.inst.expect(inst)
    c.io.rd.peekInt() should be(rd)
    c.io.rs1.peekInt() should be(rs1)
    c.io.rs2.peekInt() should be(rs2)
    c.io.imm.peekInt() should be(imm)
    c.io.toALU.peekBoolean() should be(toALU)
    c.io.branch.peekBoolean() should be(branch)
    c.io.use_imm.peekBoolean() should be(use_imm)
    c.io.jump.peekBoolean() should be(jump)
    c.io.is_load.peekBoolean() should be(load)
    c.io.is_store.peekBoolean() should be(store)
  }
}

    test(new Decoder) { c =>
      c.io.op.poke(makeBin("lbu x1, 16(x2)"))
      c.clock.step()


package chiselv

import chiseltest._
import chiseltest.experimental.expose
import com.carlosedp.riscvassembler.ObjectUtils._
import org.scalatest._

import flatspec._
import matchers._

class GPIOWrapper(bitWidth: Int = 32, numGPIO: Int = 8) extends GPIO(bitWidth, numGPIO) {
  val obs_GPIO      = expose(GPIO)
  val obs_DIRECTION = expose(direction)
}
class GPIOSpec extends AnyFlatSpec with ChiselScalatestTester with should.Matchers {
  def defaultDut =
    test(new GPIOWrapper(32, 8)).withAnnotations(
      Seq(
        WriteVcdAnnotation,
        VerilatorBackendAnnotation,
      )

      c.obs_GPIO.peekInt() should be(0)
      c.obs_DIRECTION.peekInt() should be(0)
    }
  }

  it should "write direction then read" in {
    defaultDut { c =>
      c.io.GPIOPort.writeDirection.poke(true)
      c.io.GPIOPort.dataIn.poke("10101010".b)
      c.clock.step()
      c.obs_DIRECTION.peekInt() should be("10101010".b)
      c.io.GPIOPort.directionOut.peekInt() should be("10101010".b)
    }
  }

  it should "write IO data then read" in {
    defaultDut { c =>
      c.io.GPIOPort.writeValue.poke(true)
      c.io.GPIOPort.dataIn.poke("11111111".b)
      c.clock.step()
      c.obs_GPIO.peekInt() should be("11111111".b)
      c.io.GPIOPort.writeDirection.poke(true)
      c.io.GPIOPort.dataIn.poke("01010101".b)
      c.clock.step()
      c.io.GPIOPort.valueOut.peekInt() should be("01010101".b)
      c.clock.step(5)
    }
  }

  // Doesn't work since we can't poke the analog port
  //
  // it should "read IO data from input" in {
  //   defaultDut(){ c =>
  //     c.io.GPIOPort.dataOut.peekInt() should be(0)
  //     c.io.GPIOPort.dir.poke(0.U) // Read input
  //     // c.InOut.io.dataIO.poke(1.U) // Write to input pin
  //     c.clock.step()
  //     c.io.GPIOPort.dataOut.peekInt() should be(1)
  //   }
  // }

}

    )

  it should "read GPIO output and direction as 0 when initialized" in {


package chiselv

import chiseltest._
import org.scalatest._

import flatspec._
import matchers._

class MemorySpec extends AnyFlatSpec with ChiselScalatestTester with should.Matchers {

  it should "write and read from address" in {
    test(new DualPortRAM(32, 1 * 1024)).withAnnotations(
      Seq(
        WriteVcdAnnotation
      )
    ) { c =>
      c.io.writeEnable.poke(true)
      c.io.writeAddress.poke(1)
      c.io.writeData.poke(1234)
      c.clock.step(1)
      c.io.readAddress.poke(1)
      c.clock.step(2)
      c.io.readData.peekInt() should be(1234)
    }
  }
  it should "not allow write to address" in {
    test(new DualPortRAM(32, 1 * 1024)) { c =>
      c.io.writeAddress.poke(0x00000010)
      c.io.readAddress.poke(0x00000010)
      c.clock.step()
      c.io.writeData.poke(1234)
      c.clock.step()
      c.io.readData.peekInt() should be(0)
    }
  }

  it should "write data and follow with a read in same address" in {
    test(new DualPortRAM(32, 64 * 1024)) { c =>
      val addressOffset = 0x0000100L
      val addresses     = Seq(0x0L, 0x0010L, 0x0d00L, 0x1000L, 0x2000L, 0x8000L, 0xe000L)
      val values        = Seq(0, 1, 0x0000_cafeL, 0xbaad_cafeL, 0xffff_ffffL)

          c.io.readData.peekInt() should be(value)
        }
      }
      c.clock.step(10)
    }
  }

  behavior of "InstructionMemory"
  it should "load from file and read multiple instructions" in {
    val filename = "MemorySpecTestFile.hex"
    // Create memory test file with 32bit address space
    val file = os.pwd / "MemorySpecTestFile.hex"
    os.remove(file)
    os.write(file, "00010203\r\n08090A0B\r\nDEADBEEF\r\n07060504\r\n")
    test(new InstructionMemory(32, 16 * 1024, filename)).withAnnotations(Seq(WriteVcdAnnotation)) { c =>
      c.io.readAddr.poke(0)
      c.clock.step()
      c.io.readData.peekInt() should be(0x00010203L)
      c.io.readAddr.poke(4)
      c.clock.step()
      c.io.readData.peekInt() should be(0x08090a0bL)
      c.io.readAddr.poke(8)
      c.clock.step()
      c.io.readData.peekInt() should be(0xdeadbeefL)
      c.io.readAddr.poke(12)
      c.clock.step()
      c.clock.step()
      c.io.readData.peekInt() should be(0x07060504L)
      c.io.readAddr.poke(1)
      c.clock.step()
    }
    os.remove.all(file)
  }
}

      addresses.foreach { address =>
        values.foreach { value =>
          c.io.writeEnable.poke(true)
          c.io.writeAddress.poke(addressOffset + address)
          c.io.readAddress.poke(addressOffset + address)


package chiselv

import chiseltest._
import org.scalatest._

import flatspec._
import matchers._

class ProgramCounterSpec extends AnyFlatSpec with ChiselScalatestTester with should.Matchers {

  it should "initialize to 0" in {
    test(new ProgramCounter) { c =>
      c.io.PC.peekInt() should be(0)
    }
  }
  it should "initialize to 0x00400000" in {
    test(new ProgramCounter(entryPoint = 0x00400000)) { c =>
      c.io.PC.peekInt() should be(0x00400000L)
    }
  }
  it should "walk 4 bytes" in {
    test(new ProgramCounter) { c =>
      c.io.PC.peekInt() should be(0)
      c.io.PC4.peekInt() should be(4)

      c.io.writeEnable.poke(true)
      c.io.dataIn.poke(c.io.PC4.peek())
<TARGET>
      c.io.PC.peekInt() should be(4)
    }
  }
  it should "jump to 0xbaddcafe (write)" in {
    test(new ProgramCounter) { c =>
      c.io.writeEnable.poke(true)
      c.io.dataIn.poke(0xbaddcafeL)
      c.clock.step()
      c.io.PC.peekInt() should be(0xbaddcafeL)
    }
  }
  it should "add 32 to PC ending up in 40" in {
    test(new ProgramCounter) { c =>
      c.io.writeEnable.poke(true)
      c.io.dataIn.poke(8)
      c.clock.step()
      c.io.PC.peekInt() should be(8)
      c.io.writeAdd.poke(true)
      c.io.dataIn.poke(32)
      c.clock.step()
      c.io.PC.peekInt() should be(40)
    }
  }
}


package chiselv

import scala.util.Random
import chisel3._
import chiseltest._
import org.scalatest._

import flatspec._
import matchers._

class RegisterBankSpec extends AnyFlatSpec with ChiselScalatestTester with should.Matchers {

  it should "have x0 equal to 0" in {

      c.io.writeEnable.poke(true)
      c.io.regwr_addr.poke(0.U)
      c.io.regwr_data.poke(123.U)
      c.clock.step()
      c.io.rs1_addr.peekInt() should be(0)
    }
  }
  it should "not write if not enabled" in {
    test(new RegisterBank) { c =>
      c.io.rs1_addr.poke(1)
      c.io.regwr_addr.poke(1)
      c.io.regwr_data.poke(123.U)
      c.clock.step()
      c.io.rs1.expect(0.U)
    }
  }
  it should "write all other registers and read values as expected" in {
    test(new RegisterBank).withAnnotations(Seq(WriteVcdAnnotation)) { c =>
      val one = BigInt(1)
      val max = (one << 32) - one
      val cases = Array[BigInt](1, 2, 4, 123, 0, 0x7fffffffL, max) ++ Seq.fill(10)(
        BigInt(Random.nextLong(max.toLong))
      )
      Random.shuffle(1 to 31).foreach { i =>
        cases.foreach { v =>
          c.io.writeEnable.poke(true)
          c.clock.step()
          c.io.regwr_addr.poke(i)
          c.io.regwr_data.poke(v.U)
          c.io.rs2_addr.poke(i)
          c.clock.step()
          c.io.writeEnable.poke(false)
          c.clock.step()
          c.io.rs2.expect(v)
        }
      }
      c.clock.step(10)
    }
  }
}

    test(new RegisterBank) { c =>
      c.io.rs1_addr.poke(0.U)
      c.io.rs1.expect(0.U)
    }


package chiselv

import chiseltest._
import org.scalatest._

import flatspec._
import matchers._

class SysconSpec extends AnyFlatSpec with ChiselScalatestTester with should.Matchers {

  def defaultDut =
    test(new Syscon(32, 50000000, 8, 0L, 64 * 1024, 64 * 1024)).withAnnotations(
      Seq(
        WriteVcdAnnotation
      )
    )

  it should "read dummy value from Syscon 0x0" in {
    defaultDut { c =>
      c.io.Address.poke(0x00)
      c.clock.step()
      c.io.DataOut.peekInt() should be(0xbaad_cafeL)
    }
  }
  it should "read clock speed from Syscon" in {
    defaultDut { c =>
      c.io.Address.poke(0x08)
      c.clock.step()
      c.io.DataOut.peekInt() should be(50000000)
    }
  }
  it should "check if UART0 is available in Syscon" in {
    defaultDut { c =>
      c.io.Address.poke(0x10)
<TARGET>
  it should "check if GPIO0 is available in Syscon" in {
    defaultDut { c =>
      c.io.Address.poke(0x18)

  it should "check num of GPIO0s in Syscon" in {
    defaultDut { c =>
      c.io.Address.poke(0x28)
      c.clock.step()
      c.io.DataOut.peekInt() should be(8)
    }
  }
  it should "check if Timer0 is available in Syscon" in {
    defaultDut { c =>
      c.io.Address.poke(0x24)
      c.clock.step()
      c.io.DataOut.peekInt() should be(1)
    }
  }
  it should "check RAM size in Syscon" in {
    defaultDut { c =>
      c.io.Address.poke(0x34)
      c.clock.step()
      c.io.DataOut.peekInt() should be(64 * 1024)
    }
  }
}


package chiselv

import chiseltest._
import chiseltest.experimental._
import org.scalatest._
import chiselv.Timer

import flatspec._
import matchers._

class TimerWrapper(bitWidth: Int = 32, cpuFrequency: Int) extends Timer(bitWidth, cpuFrequency) {
  val obs_counter = expose(counter)
}
class TimerSpec extends AnyFlatSpec with ChiselScalatestTester with should.Matchers {

  val cpuFrequency = 25000000
  def defaultDut =
    test(new TimerWrapper(32, cpuFrequency)).withAnnotations(
      Seq(
        // WriteVcdAnnotation
      )
    )

  val ms = cpuFrequency / 1000
  it should "read timer after 1ms" in {
    defaultDut { c =>
      c.clock.setTimeout(0)

  it should "reset timer after 3ms and continue counting" in {
    defaultDut { c =>
      c.clock.setTimeout(0)
      c.clock.step(3 * ms)
      c.obs_counter.peekInt() should be(3)
      c.io.dataOut.peekInt() should be(3)
      c.io.writeEnable.poke(true)
      c.io.dataIn.poke(0)
      c.clock.step()
      c.io.writeEnable.poke(false)
      c.obs_counter.peekInt() should be(0)
      c.obs_counter.peekInt() should be(0)
      c.clock.step(ms)
      c.io.dataOut.peekInt() should be(1)
      c.obs_counter.peekInt() should be(1)
    }
  }
}

      c.io.dataOut.peekInt() should be(0)
      c.obs_counter.peekInt() should be(0)
      c.clock.step(ms)
      c.io.dataOut.peekInt() should be(1)


package chiselv

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

import flatspec._
import matchers._

/*
 * This file has been provided by Anton Blanchard's Chiselwatt repository at:
 * https://github.com/antonblanchard/chiselwatt
 */

class UartSpec extends AnyFlatSpec with ChiselScalatestTester with should.Matchers {
  val rxOverclock = 16
  val fpgaClock   = 15000000
  val baudRate    = 115200
  val divider     = Math.round(1.0f * fpgaClock / (baudRate * rxOverclock) - 1)

  private def rxOne(u: Uart, c: UInt) = {
    /* Start bit */
    u.io.serialPort.rx.poke(0.U)
    clockSerial(u.clock)

    /*
     * This doesn't work:
     * c.pad(8).asBools.foreach({cc =>
     */
    c.asBools.foreach { cc =>
      u.io.serialPort.rx.poke(cc)
      clockSerial(u.clock)
    }

    /* Have to do this instead: */
    u.io.serialPort.rx.poke(0.U)
    clockSerial(u.clock)

    /* Stop bit */
    u.io.serialPort.rx.poke(1.U)
    clockSerial(u.clock)
  }

  it should "pass a unit test" in {
    test(new Uart(64, rxOverclock)).withAnnotations(Seq(WriteVcdAnnotation)) { u =>
      u.clock.setTimeout(10000)

      u.io.dataPort.clockDivisor.valid.poke(true.B)
      u.io.dataPort.clockDivisor.bits.poke(divider.U)

      u.io.dataPort.rxQueue.ready.poke(false.B)

      val testChars = Seq("h41".U, "h6E".U, "h74".U, "h6F".U, "h6E".U)

      testChars.foreach(c => rxOne(u, c))

      u.io.dataPort.rxQueue.ready.poke(true.B)
      testChars.foreach { c =>
        u.io.dataPort.rxQueue.valid.expect(true.B)
        u.io.dataPort.rxQueue.bits.expect(c)
        u.clock.step()
      }
      u.io.dataPort.rxQueue.ready.poke(false.B)

      u.io.dataPort.rxFull.expect(false.B)
      u.io.dataPort.txFull.expect(false.B)

      rxOne(u, "h5a".U)
      u.io.dataPort.rxFull.expect(false.B)
      (0 to 64).foreach(_ => rxOne(u, "h5a".U))
      u.io.dataPort.rxFull.expect(true.B)

      u.io.dataPort.txQueue.bits.poke("h75".U)
      u.io.dataPort.txQueue.valid.poke(true.B)
      u.clock.step()
      u.io.dataPort.txFull.expect(false.B)
      (0 to 64).foreach(_ => u.clock.step())
      u.io.dataPort.txFull.expect(true.B)
    }
  }
}

def clockSerial(clk: Clock) = clk.step(fpgaClock / baudRate)