Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor simd test #5

Merged
merged 2 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
6 changes: 3 additions & 3 deletions src/test/scala/simd/PETest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class PEManualTest
double_round: Bool,
golden_output: Byte
) = {
dut.clock.step()
dut.clock.step(1)

// giving input data
dut.io.input_i.poke(input)
Expand All @@ -117,15 +117,15 @@ class PEManualTest
dut.io.ctrl_i.max_int_i.poke(max_int)
dut.io.ctrl_i.min_int_i.poke(min_int)
dut.io.ctrl_i.double_round_i.poke(double_round)
dut.clock.step()
dut.clock.step(1)

// grab the output data
val out = dut.io.out_o.peekInt()

// assert the PE's output data equals to the golden data
assert(out == golden_output)

dut.clock.step()
dut.clock.step(1)
}

// manually random data test
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())

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOL, randomization is based on time. Interesting haha


// 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(1)
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(1)

}

// 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(1)

// 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(1)

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(1)
}

}

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 = 100

// 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
)
}
}
}
}