diff --git a/src/main/scala/simd/SIMD.scala b/src/main/scala/simd/SIMD.scala index 22d1d6f..7c6dcd9 100644 --- a/src/main/scala/simd/SIMD.scala +++ b/src/main/scala/simd/SIMD.scala @@ -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 diff --git a/src/test/scala/simd/PETest.scala b/src/test/scala/simd/PETest.scala index 704912f..2d1e2d7 100644 --- a/src/test/scala/simd/PETest.scala +++ b/src/test/scala/simd/PETest.scala @@ -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) @@ -117,7 +117,7 @@ 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() @@ -125,7 +125,7 @@ class PEManualTest // 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 diff --git a/src/test/scala/simd/SIMDTest.scala b/src/test/scala/simd/SIMDTest.scala index bea85aa..a27fe31 100644 --- a/src/test/scala/simd/SIMDTest.scala +++ b/src/test/scala/simd/SIMDTest.scala @@ -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(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 + ) + } } } }