summaryrefslogtreecommitdiff
path: root/src/test/scala/chiselTests
diff options
context:
space:
mode:
authoranniej-sifive2021-08-03 16:09:19 -0700
committerGitHub2021-08-03 16:09:19 -0700
commitda923f317ff325a93cee6289552ccfa413c35f98 (patch)
tree59f30f6d3e11bc5fb533eafe163c255525885915 /src/test/scala/chiselTests
parent1e7829eb674eed85a4cd537896d9fd9ee0bc5ff4 (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/scala/chiselTests')
-rw-r--r--src/test/scala/chiselTests/QueueFlushSpec.scala259
-rw-r--r--src/test/scala/chiselTests/QueueSpec.scala8
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)
}
}
}