Skip to content

Commit

Permalink
refactor simd test
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoling-yi committed Jan 20, 2024
1 parent 686870c commit a3f94a7
Show file tree
Hide file tree
Showing 2 changed files with 218 additions and 53 deletions.
4 changes: 2 additions & 2 deletions src/main/scala/simd/SIMD.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,12 @@ class SIMD(laneLen: Int = SIMDConstant.laneLen)

// if data out is valid from PEs, store the results in case later needs keep sending output data if receiver side is not ready
when(lane(0).io.valid_o) {
out_reg := Cat(result)
out_reg := Cat(result.reverse)
}

// concat every result to a big data bus for output
// if is keep sending output, send the stored result
io.data.out_o.bits := Mux(keep_output, out_reg, Cat(result))
io.data.out_o.bits := Mux(keep_output, out_reg, Cat(result.reverse))

// first valid from PE or keep sending valid if receiver side is not ready
io.data.out_o.valid := lane(0).io.valid_o || keep_output
Expand Down
267 changes: 216 additions & 51 deletions src/test/scala/simd/SIMDTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,68 +7,233 @@ import scala.math.BigInt
import org.scalatest.matchers.should.Matchers
import org.scalatest.Tag

// post-processing SIMD manually-generated random data test
// TODO: automated a brunch of random test data (parallel) generation and check
class SIMDManualTest
import scala.util.Random

// a trait including SIMD test util functions
trait HasSIMDTestUtils
extends HasRandomTestGen
with HasPostProcessingGoldenModel {

// generate random input data sequence
def InputSeqGen(): Seq[Int] = {
// Initialize random seed
val random = new Random(System.currentTimeMillis())

// Generate random values
val input: Seq[Int] =
Seq.fill(SIMDConstant.laneLen)(random.nextInt(math.pow(2, 28).toInt))

input

}

// random test data generation
def TestGen(): (Seq[Int], Byte, Byte, Int, Byte, Byte, Byte, Boolean) = {
// generate random input data sequence
val input = InputSeqGen()
// control data generation, all the input sequence share one set control data
val (
_,
inputZp,
outputZp,
multiplier,
shift,
maxInt,
minInt,
doubleRound
) = RandomTestGen()
// return the generated test data
(input, inputZp, outputZp, multiplier, shift, maxInt, minInt, doubleRound)

}

// input data sequence to a big bus
def vec2bus(input: Seq[Int]): BigInt = {

var flattenedUInt = ""

for (i <- input) {
flattenedUInt = String.format("%08X", i) + flattenedUInt
}

BigInt(flattenedUInt, 16)
}

// golden data sequence generation
def goldenGen(
input: Seq[Int],
inputZp: Byte,
outputZp: Byte,
multiplier: Int,
shift: Byte, // values between 0-63
maxInt: Byte,
minInt: Byte,
doubleRound: Boolean
) = {

// map input data sequence to results data sequence
val result = input.map(i =>
postProcessingGoldenModel(
i,
inputZp,
outputZp,
multiplier,
shift,
maxInt,
minInt,
doubleRound
)
)

// change to array type
result.toArray
}

// split dut output big bus to result array for comparison
def bus2vec(output: UInt) = {
var result = Array.ofDim[Byte](SIMDConstant.laneLen)
// convert corresponding bits to Byte data type
for (i <- 0 until SIMDConstant.laneLen) {
result(i) = output(
(i + 1) * SIMDConstant.outputType,
i * SIMDConstant.outputType
).litValue.toByte
}
result
}

// give input data big bus to dut
def giveInputData[T <: SIMD](dut: T, input: BigInt) = {
// giving input data
dut.clock.step()
dut.io.data.input_i.bits.poke(input)
dut.io.data.input_i.valid.poke(1.B)
while (dut.io.data.input_i.ready.peekBoolean() == false) {
dut.clock.step(1)
}
dut.clock.step()

}

// get output form dut and change to array type then verify with golden data
def checkSIMDOutput[T <: SIMD](
dut: T,
goldenValue: Array[Byte]
) = {

// manually check SIMD output
dut.io.data.out_o.ready.poke(1.B)
while (dut.io.data.out_o.valid.peekBoolean() == false) {
dut.clock.step(1)
}
val out = dut.io.data.out_o.bits.peek()
// change big bus to array type
val outSeq = bus2vec(out)

// output array data verify with golden data
(outSeq zip goldenValue).foreach { case (out, golden) =>
assert(out == golden)
}

}

// function wrapper for sending the configuration and the input data to the SIMD
def verify[T <: SIMD](
dut: T,
input: BigInt,
input_zp: Byte,
output_zp: Byte,
multiplier: Int,
shift: Byte,
max_int: Byte,
min_int: Byte,
doubleRound: Bool,
goldenValue: Array[Byte]
) = {
dut.clock.step()

// giving the configuration
dut.io.ctrl.bits.input_zp_i.poke(input_zp)
dut.io.ctrl.bits.output_zp_i.poke(output_zp)
dut.io.ctrl.bits.multiplier_i.poke(multiplier)
dut.io.ctrl.bits.shift_i.poke(shift)
dut.io.ctrl.bits.max_int_i.poke(max_int)
dut.io.ctrl.bits.min_int_i.poke(min_int)
dut.io.ctrl.bits.double_round_i.poke(doubleRound)
dut.io.ctrl.valid.poke(1.B)
while (dut.io.ctrl.ready.peekBoolean() == false) {
dut.clock.step(1)
}
dut.clock.step()

dut.io.ctrl.valid.poke(0)

// give input data big bus to dut
giveInputData(dut, input)

// get output and check
checkSIMDOutput(dut, goldenValue)

dut.clock.step()
}

}

class SIMDAutoTest
extends AnyFlatSpec
with ChiselScalatestTester
with Matchers {
with Matchers
with HasSIMDTestUtils {
"DUT" should "pass" in {
test(new SIMD)
.withAnnotations(
Seq(WriteVcdAnnotation)
) { dut =>
// function wrapper for sending the configuration and the input data to the SIMD
def verify(
input: BigInt,
input_zp: Byte,
output_zp: Byte,
multiplier: Int,
shift: Byte,
max_int: Byte,
min_int: Byte
) = {
dut.clock.step()

// giving the configuration
dut.io.ctrl.bits.input_zp_i.poke(input_zp)
dut.io.ctrl.bits.output_zp_i.poke(output_zp)
dut.io.ctrl.bits.multiplier_i.poke(multiplier)
dut.io.ctrl.bits.shift_i.poke(shift)
dut.io.ctrl.bits.max_int_i.poke(max_int)
dut.io.ctrl.bits.min_int_i.poke(min_int)
dut.io.ctrl.bits.double_round_i.poke(1)
dut.io.ctrl.valid.poke(1.B)
dut.clock.step()
dut.io.ctrl.valid.poke(0)

// giving input data
dut.clock.step()
dut.io.data.input_i.bits.poke(input)
dut.clock.step()

// manually check SIMD output
val out = dut.io.data.out_o.bits.peekInt() & ((1 << 8) - 1)
println(out)
val out1 = dut.io.data.out_o.bits.peekInt() & (((1 << 8) - 1) << 8)
println(out1)

dut.clock.step()
}
// set test number
val testNum = 1

// function to translate integer to hex for packing two integer into one big BigInt
def int2hex(width: Int, intValue: Int) = {
val paddingChar = '0'
f"$intValue%x".reverse.padTo(width, paddingChar).reverse
}
// random test data generation
for (i <- 1 to testNum) {
val (
input,
inputZp,
outputZp,
multiplier,
shift,
maxInt,
minInt,
doubleRound
) = TestGen()

// random data test
var a = BigInt(int2hex(8, 267082502) + int2hex(8, 267082502), 16)
verify(a, -59, -118, 8192, 34, 127, -128)
// input data sequence to a big bus
val inputBus = vec2bus(input)

a = BigInt(int2hex(8, 71671912) + int2hex(8, 71671912), 16)
verify(a, -23, -126, 65536, 37, 127, -128)
// golden value gen
val goldenValue = goldenGen(
input,
inputZp,
outputZp,
multiplier,
shift,
maxInt,
minInt,
doubleRound
)

// give dut test data and verify result
verify(
dut,
inputBus,
inputZp,
outputZp,
multiplier,
shift,
maxInt,
minInt,
doubleRound.B,
goldenValue
)
}
}
}
}

0 comments on commit a3f94a7

Please sign in to comment.