Commit 187a74fb authored by Jens Korinth's avatar Jens Korinth
Browse files

Started with property-based testing

parent 63498417
......@@ -15,12 +15,9 @@ import chisel3.util._
* @param littleEndian if inWidth > outWidth, determines
* the order of the nibbles (low to high)
**/
class DataWidthConverter(
val inWidth: Int,
val outWidth: Int,
val littleEndian: Boolean = true
) extends Module {
class DataWidthConverter(val inWidth: Int,
val outWidth: Int,
val littleEndian: Boolean = true) extends Module {
require (inWidth > 0, "inWidth must be > 0")
require (outWidth > 0, "inWidth must be > 0")
require (inWidth != outWidth, "inWidth (%d) must be different from outWidth (%d)"
......
......@@ -2,18 +2,24 @@ package chisel.miscutils
import chisel3._
import chisel3.util._
sealed case class Signal(value: Boolean, periods: Int = 1) extends Ordered[Signal] {
import scala.math.Ordered.orderingToOrdered
def compare(that: Signal): Int = periods compare that.periods
}
object SignalGenerator {
type Waveform = List[Signal]
sealed case class Signal(value: Boolean, periods: Int = 1) extends Ordered[Signal] {
import scala.math.Ordered.orderingToOrdered
def compare(that: Signal): Int = periods compare that.periods
def unary_! = this.copy(value = !value)
}
final case class Waveform(signals: Seq[Signal]) {
require (signals.length > 0, "waveform must not be empty")
}
implicit def waveformToSeq(w: Waveform): Seq[Signal] = w.signals
implicit def seqToWaveform(s: Seq[Signal]): Waveform = Waveform.apply(s)
implicit def makeSignal(sd: (Boolean, Int)): Signal = Signal(sd._1, sd._2)
implicit def makeWaveform(ls: List[(Boolean, Int)]): Waveform = ls map makeSignal
}
class SignalGenerator(signals: SignalGenerator.Waveform, useInputAsClock: Boolean = false) extends Module {
class SignalGenerator(val signals: SignalGenerator.Waveform,
val useInputAsClock: Boolean = false) extends Module {
require (signals.length > 0, "Waveform must not be empty.")
require (signals map (_.periods > 1) reduce (_&&_),
"All signals must have at least two clock cycles length.")
......@@ -29,7 +35,7 @@ class SignalGenerator(signals: SignalGenerator.Waveform, useInputAsClock: Boolea
when (reset) {
curr_idx := 0.U
cnt := cnts_rom(0)
vreg := 0.U
vreg := vals_rom(0)
}
.otherwise {
vreg := vals_rom(curr_idx)
......
package chisel.miscutils
import generators._
import org.scalacheck._, org.scalacheck.Prop._
import org.scalatest.prop.Checkers
import chisel3._
import chisel3.iotesters.{ChiselFlatSpec, Driver, PeekPokeTester}
class SignalGeneratorWaveformTest(sg: SignalGenerator) extends PeekPokeTester(sg) {
private var cc = 0
// re-define step to output progress info
override def step(n: Int) {
if (sg.useInputAsClock) {
super.step(1)
poke(sg.io.in, (peek(sg.io.in) + 1) % 2)
super.step(1)
cc += 1
} else {
super.step(n)
cc += n
}
}
poke(sg.io.in, 0)
reset(10)
step(10)
cc = 0
for (i <- 0 until sg.signals.length) {
println(s"signal ${i+1}/${sg.signals.length} started at $cc")
for (s <- 0 until sg.signals(i).periods) {
val e = sg.signals(i).value
val p = peek(sg.io.v)
expect(sg.io.v, sg.signals(i).value.B, s"expected $e at clock cycle $cc")
step(1)
}
println(s"signal ${i+1}/${sg.signals.length} finished at $cc")
step(1)
}
}
class SignalGeneratorSpec extends ChiselFlatSpec with Checkers {
behavior of "SignalGenerator"
it should "generate arbitrary waveforms" in
check(forAll(signalGeneratorGen) { case (wave, useInputAsClock) =>
println(s"useInputAsClock: $useInputAsClock, wave: $wave")
Driver.execute(Array("--fint-write-vcd", "--target-dir", "test/signalgenerator/spec"),
() => new SignalGenerator(wave, useInputAsClock))
{ m => new SignalGeneratorWaveformTest(m) }
})
}
......@@ -57,7 +57,7 @@ class SignalGeneratorTest(sg: SignalGenerator) extends PeekPokeTester(sg) {
for (j <- 0 to 1) {
for (i <- 2 until 30) {
waitForPosEdge(sg.io.v)
//waitForPosEdge(sg.io.v)
val cc_start = cc
waitForNegEdge(sg.io.v)
expect (cc - cc_start == i, "wrong number of clock cycles")
......
package chisel.miscutils
import org.scalacheck._
import SignalGenerator._
import scala.language.implicitConversions
package object generators {
final case class Limited[A](a: A, min: A, max: A)(implicit num: Numeric[A]) {
import num._
require (a >= min, s"$a must be >= $min")
require (a <= max, s"$a must be <= $max")
}
implicit def limitedToA[A](l: Limited[A]): A = l.a
def genLimited[A](min: A, max: A)(implicit num: Numeric[A], c: Gen.Choose[A]): Gen[Limited[A]] =
Gen.choose(min, max) map (v => Limited.apply(v, min, max))
implicit def shrinkLimited[A](l: Limited[A])(implicit num: Numeric[A]): Shrink[Limited[A]] = Shrink { l =>
import num._
if (l.a <= l.min) Stream.empty[Limited[A]] else Stream(Limited(l.a - num.one, l.min, l.max))
}
type BitWidth = Limited[Int]
type DataSize = Limited[Int]
def bitWidthGen(max: Int = 64): Gen[BitWidth] = genLimited(1, max)
def dataSizeGen(max: Int = 1024): Gen[DataSize] = genLimited(1, max)
def widthConversionGen(max: Int = 64): Gen[(BitWidth, BitWidth, Boolean)] = for {
inWidth <- bitWidthGen(max)
outWidth <- bitWidthGen(max)
littleEndian <- Arbitrary.arbitrary[Boolean]
} yield (inWidth, outWidth, littleEndian)
def dataSourceGen(maxWidth: Int = 64, maxSize: Int = 1024): Gen[(BitWidth, DataSize, Boolean)] = for {
bw <- bitWidthGen(maxWidth)
ds <- dataSizeGen(maxSize)
r <- Arbitrary.arbitrary[Boolean]
} yield (bw, ds, r)
def signalGen(maxLength: Int = 15): Gen[Signal] = for {
v <- Arbitrary.arbitrary[Boolean]
p <- genLimited(2, maxLength)
} yield Signal(v, p)
def alternate(ss: Seq[Signal]): Seq[Signal] = ss match {
case s0 +: s1 +: sr => s0 +: alternate(s1.copy(value = !s0.value) +: sr)
case s +: sr => s +: sr
case Seq() => Seq()
}
def waveformGen(maxLength: Int = 20): Gen[Waveform] = Gen.sized { n =>
Gen.nonEmptyBuildableOf[Seq[Signal], Signal](signalGen()) map (ss => Waveform(alternate(ss)))
}
implicit def waveformShrink(waveform: Waveform): Shrink[Waveform] =
Shrink { w => if (w.length <= 1) Stream.empty[Waveform] else Stream(w.drop(1)) }
def signalGeneratorGen: Gen[(Waveform, Boolean)] = for {
w <- waveformGen()
i <- Arbitrary.arbitrary[Boolean]
} yield (w, true)
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment