diff options
| author | anniej-sifive | 2021-08-03 16:09:19 -0700 |
|---|---|---|
| committer | GitHub | 2021-08-03 16:09:19 -0700 |
| commit | da923f317ff325a93cee6289552ccfa413c35f98 (patch) | |
| tree | 59f30f6d3e11bc5fb533eafe163c255525885915 /src/test | |
| parent | 1e7829eb674eed85a4cd537896d9fd9ee0bc5ff4 (diff) | |
Added flush capability to Queue (#2030)
Co-authored-by: Megan Wachs <megan@sifive.com>
Co-authored-by: Deborah Soung <debs@sifive.com>
Diffstat (limited to 'src/test')
| -rw-r--r-- | src/test/scala/chiselTests/QueueFlushSpec.scala | 259 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/QueueSpec.scala | 8 |
2 files changed, 263 insertions, 4 deletions
diff --git a/src/test/scala/chiselTests/QueueFlushSpec.scala b/src/test/scala/chiselTests/QueueFlushSpec.scala new file mode 100644 index 00000000..11a411a8 --- /dev/null +++ b/src/test/scala/chiselTests/QueueFlushSpec.scala @@ -0,0 +1,259 @@ +package chiselTests + +import org.scalacheck._ + +import chisel3._ +import chisel3.testers.{BasicTester, TesterDriver} +import chisel3.util._ +import chisel3.util.random.LFSR +import treadle.WriteVcdAnnotation + +/** Test elements can be enqueued and dequeued when flush is tied to false + * + * @param elements The sequence of elements used in the queue + * @param queueDepth The max number of entries in the queue + * @param bitWidth Integer size of the data type used in the queue + * @param tap Integer tap('seed') for the LFSR + * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element + */ +class ThingsPassThroughFlushQueueTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends ThingsPassThroughTester(elements, queueDepth, bitWidth, tap, useSyncReadMem, hasFlush = true) + +/** Generic flush queue tester base class + * + * @param elements The sequence of elements used in the queue + * @param queueDepth The max number of entries in the queue + * @param bitWidth Integer size of the data type used in the queue + * @param tap Integer tap('seed') for the LFSR + * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element + */ +abstract class FlushQueueTesterBase(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester { + val q = Module(new Queue(UInt(bitWidth.W), queueDepth, hasFlush = true)) + val elems = VecInit(elements.map(_.U)) + val inCnt = Counter(elements.length + 1) + val outCnt = RegInit(0.U(log2Ceil(elements.length).W)) + val currQCnt = RegInit(0.U(log2Ceil(5).W)) + + val flush: Bool = WireInit(false.B) + val flushRegister = RegNext(flush, init = false.B) + q.io.flush.get := flush + q.io.enq.valid := (inCnt.value < elements.length.U) + q.io.deq.ready := LFSR(16)(tap) + + q.io.enq.bits := elems(inCnt.value) + when(q.io.enq.fire()) { + inCnt.inc() + currQCnt := currQCnt + 1.U //counts how many items have been enqueued + } + when(q.io.deq.fire()) { + assert(flushRegister === false.B) //check queue isn't flushed (can't dequeue an empty queue) + } + when(flushRegister) { //Internal signal maybe_full is a register so some signals update on the next cycle + //check that queue gets flushed when queue is full + assert(q.io.count === 0.U) + assert(!q.io.deq.valid, "Expected to not be able to dequeue when flush is asserted the previous cycle") + assert(q.io.enq.ready, "Expected enqueue to be ready when flush was asserted the previous cycle because queue should be empty") + } + when(inCnt.value === elements.length.U) { //stop when all entries are enqueued + stop() + } +} + +/** Test queue can flush at random times + * + * @param elements The sequence of elements used in the queue + * @param queueDepth The max number of entries in the queue + * @param bitWidth Integer size of the data type used in the queue + * @param tap Integer tap('seed') for the LFSR + * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element + */ +class QueueGetsFlushedTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends FlushQueueTesterBase(elements, queueDepth, bitWidth, tap, useSyncReadMem) { + flush := LFSR(16)((tap + 3) % 16) //testing a flush when flush is called randomly + val halfCnt = (queueDepth + 1)/2 + + when(q.io.deq.fire()) { + //ensure that what comes out is what comes in + assert(currQCnt <= queueDepth.U) + assert(elems(outCnt) === q.io.deq.bits) + outCnt := outCnt + 1.U + when (currQCnt > 0.U) { + currQCnt := Mux(q.io.enq.fire(), currQCnt, (currQCnt - 1.U)) + } + } + when(flush) { + assert(currQCnt === 0.U || q.io.deq.valid) + outCnt := outCnt + Mux(q.io.enq.fire(), (currQCnt + 1.U), currQCnt) + currQCnt := 0.U //resets the number of items currently inside queue + } +} + +/** Test queue can flush when empty + * + * @param elements The sequence of elements used in the queue + * @param queueDepth The max number of entries in the queue + * @param bitWidth Integer size of the data type used in the queue + * @param tap Integer tap('seed') for the LFSR + * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element + */ +class EmptyFlushEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends FlushQueueTesterBase(elements, queueDepth, bitWidth, tap, useSyncReadMem) { + val cycleCounter = Counter(elements.length + 1) + cycleCounter.inc() //counts every cycle + + //testing a flush when queue is empty + flush := (cycleCounter.value === 0.U && inCnt.value === 0.U) //flushed only before anything is enqueued + q.io.enq.valid := (inCnt.value < elements.length.U) && !flush + + when(q.io.deq.fire()) { + assert(elems(outCnt) === q.io.deq.bits) + outCnt := outCnt + 1.U + } +} + +/** Test queue can enqueue during a flush + * + * @param elements The sequence of elements used in the queue + * @param queueDepth The max number of entries in the queue + * @param bitWidth Integer size of the data type used in the queue + * @param tap Integer tap('seed') for the LFSR + * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element + */ +class EnqueueEmptyFlushEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends FlushQueueTesterBase(elements, queueDepth, bitWidth, tap, useSyncReadMem) { + val cycleCounter = Counter(elements.length + 1) + val outCounter = Counter(elements.length + 1) + + //testing an enqueue during a flush + flush := (cycleCounter.value === 0.U && inCnt.value === 0.U) //flushed only before anything is enqueued + cycleCounter.inc() //counts every cycle + + when(q.io.deq.fire()) { + //flush and enqueue were both active on the first cycle, + //so that element is flushed immediately which makes outCnt off by one + assert(elems(outCounter.value + 1.U) === q.io.deq.bits) //ensure that what comes out is what comes in + outCounter.inc() + } +} + +/** Test queue can flush when full + * + * @param elements The sequence of elements used in the queue + * @param queueDepth The max number of entries in the queue + * @param bitWidth Integer size of the data type used in the queue + * @param tap Integer tap('seed') for the LFSR + * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element + */ +class FullQueueFlushEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends FlushQueueTesterBase(elements, queueDepth, bitWidth, tap, useSyncReadMem) { + + //testing a flush when queue is full + flush := (currQCnt === queueDepth.U) + + when(q.io.deq.fire()) { + //ensure that what comes out is what comes in + assert(currQCnt <= queueDepth.U) + assert(elems(outCnt) === q.io.deq.bits) + outCnt := outCnt + 1.U + when (currQCnt > 0.U) { + currQCnt := currQCnt - 1.U + } + } + when(flush) { + outCnt := outCnt + currQCnt + currQCnt := 0.U //resets the number of items currently inside queue + assert(currQCnt === 0.U || q.io.deq.valid) + } +} + +/** Test queue can dequeue on the same cycle as a flush + * + * @param elements The sequence of elements used in the queue + * @param queueDepth The max number of entries in the queue + * @param bitWidth Integer size of the data type used in the queue + * @param tap Integer tap('seed') for the LFSR + * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element + */ +class DequeueFullQueueEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends FlushQueueTesterBase(elements, queueDepth, bitWidth, tap, useSyncReadMem) { + //Queue should be able to dequeue when queue is not empty and flush is high + + //testing a flush when dequeue is called + flush := currQCnt === (queueDepth/2).U + q.io.enq.valid := !flushRegister + q.io.deq.ready := flush + + when(q.io.deq.fire()) { + //ensure that what comes out is what comes in + assert(currQCnt <= queueDepth.U) + assert(elems(outCnt) === q.io.deq.bits) + assert(currQCnt > 0.U) + } + when(flush) { + //The outcount register is one count behind because the dequeue happens at the same time as the flush + outCnt := outCnt + currQCnt + 1.U + currQCnt := 0.U //resets the number of items currently inside queue + assert(currQCnt === 0.U || q.io.deq.valid) + } + when(flushRegister) { + //check that queue gets flushed when queue is full + assert(q.io.deq.fire() === false.B) + } + +} + +class QueueFlushSpec extends ChiselPropSpec { + // Disable shrinking on error. + implicit val noShrinkListVal = Shrink[List[Int]](_ => Stream.empty) + implicit val noShrinkInt = Shrink[Int](_ => Stream.empty) + + property("Queue should have things pass through") { + forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => + whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) { + assertTesterPasses { + new ThingsPassThroughFlushQueueTester(se._2, depth, se._1, tap, isSync) + } + } + } + } + property("Queue should flush when requested") { + forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => + whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) { + assertTesterPasses { + new QueueGetsFlushedTester(se._2, depth, se._1, tap, isSync) + } + } + } + } + property("Queue flush when queue is empty") { + forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => + whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) { + assertTesterPasses { + new EmptyFlushEdgecaseTester(se._2, depth, se._1, tap, isSync) + } + } + } + } + property("Test queue can enqueue during a flush") { + forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => + whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) { + assertTesterPasses { + new EnqueueEmptyFlushEdgecaseTester(se._2, depth, se._1, tap, isSync) + } + } + } + } + property("Queue flush when queue is full") { + forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => + whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) { + assertTesterPasses { + new FullQueueFlushEdgecaseTester(se._2, depth, se._1, tap, isSync) + } + } + } + } + property("Queue should be able to dequeue when flush is high") { + forAll(Gen.choose(3, 5), safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => + whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) { + assertTesterPasses ( + new DequeueFullQueueEdgecaseTester(se._2, depth, se._1, tap, isSync), + annotations = Seq(WriteVcdAnnotation) + ) + } + } + } +} diff --git a/src/test/scala/chiselTests/QueueSpec.scala b/src/test/scala/chiselTests/QueueSpec.scala index 3555a13c..51b899cb 100644 --- a/src/test/scala/chiselTests/QueueSpec.scala +++ b/src/test/scala/chiselTests/QueueSpec.scala @@ -9,8 +9,8 @@ import chisel3.testers.BasicTester import chisel3.util._ import chisel3.util.random.LFSR -class ThingsPassThroughTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester { - val q = Module(new Queue(UInt(bitWidth.W), queueDepth, useSyncReadMem = useSyncReadMem)) +class ThingsPassThroughTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean, hasFlush: Boolean) extends BasicTester { + val q = Module(new Queue(UInt(bitWidth.W), queueDepth, useSyncReadMem = useSyncReadMem, hasFlush = hasFlush)) val elems = VecInit(elements.map { _.asUInt() }) @@ -19,7 +19,7 @@ class ThingsPassThroughTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int q.io.enq.valid := (inCnt.value < elements.length.U) q.io.deq.ready := LFSR(16)(tap) - + q.io.flush.foreach { _ := false.B } //Flush behavior is tested in QueueFlushSpec q.io.enq.bits := elems(inCnt.value) when(q.io.enq.fire()) { inCnt.inc() @@ -205,7 +205,7 @@ class QueueSpec extends ChiselPropSpec { forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) { assertTesterPasses { - new ThingsPassThroughTester(se._2, depth, se._1, tap, isSync) + new ThingsPassThroughTester(se._2, depth, se._1, tap, isSync, false) } } } |
