From 2ed343e2305b7c22000f3f46fa81d73a369907eb Mon Sep 17 00:00:00 2001 From: Vladimir Milovanović Date: Mon, 8 Feb 2021 20:55:48 +0100 Subject: Parametrized Mem- & SyncReadMem-based implementation of the Queue class (#1740) * Added SyncReadMem-based implementation of the Queue class * Rework of the parametrized Queue class SyncReadMem-based implementation * Modification of the parametrized Queue class SyncReadMem-based implementation * Limiting the visibility of the read address for SyncReadMem-based Queue Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- src/main/scala/chisel3/util/Decoupled.scala | 26 ++++++++++---- src/test/scala/chiselTests/QueueSpec.scala | 56 ++++++++++++++--------------- 2 files changed, 47 insertions(+), 35 deletions(-) diff --git a/src/main/scala/chisel3/util/Decoupled.scala b/src/main/scala/chisel3/util/Decoupled.scala index 032d731d..006001ec 100644 --- a/src/main/scala/chisel3/util/Decoupled.scala +++ b/src/main/scala/chisel3/util/Decoupled.scala @@ -186,6 +186,7 @@ class QueueIO[T <: Data](private val gen: T, val entries: Int) extends Bundle * combinationally coupled. * @param flow True if the inputs can be consumed on the same cycle (the inputs "flow" through the queue immediately). * The ''valid'' signals are coupled. + * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element. * * @example {{{ * val q = Module(new Queue(UInt(), 16)) @@ -197,7 +198,8 @@ class QueueIO[T <: Data](private val gen: T, val entries: Int) extends Bundle class Queue[T <: Data](val gen: T, val entries: Int, val pipe: Boolean = false, - val flow: Boolean = false) + val flow: Boolean = false, + val useSyncReadMem: Boolean = false) (implicit compileOptions: chisel3.CompileOptions) extends Module() { require(entries > -1, "Queue must have non-negative number of entries") @@ -215,7 +217,7 @@ class Queue[T <: Data](val gen: T, val io = IO(new QueueIO(genType, entries)) - val ram = Mem(entries, genType) + val ram = if (useSyncReadMem) SyncReadMem(entries, genType, SyncReadMem.WriteFirst) else Mem(entries, genType) val enq_ptr = Counter(entries) val deq_ptr = Counter(entries) val maybe_full = RegInit(false.B) @@ -239,7 +241,15 @@ class Queue[T <: Data](val gen: T, io.deq.valid := !empty io.enq.ready := !full - io.deq.bits := ram(deq_ptr.value) + + if (useSyncReadMem) { + val deq_ptr_next = Mux(deq_ptr.value === (entries.U - 1.U), 0.U, deq_ptr.value + 1.U) + val r_addr = WireDefault(Mux(do_deq, deq_ptr_next, deq_ptr.value)) + io.deq.bits := ram.read(r_addr) + } + else { + io.deq.bits := ram(deq_ptr.value) + } if (flow) { when (io.enq.valid) { io.deq.valid := true.B } @@ -285,7 +295,8 @@ object Queue enq: ReadyValidIO[T], entries: Int = 2, pipe: Boolean = false, - flow: Boolean = false): DecoupledIO[T] = { + flow: Boolean = false, + useSyncReadMem: Boolean = false): DecoupledIO[T] = { if (entries == 0) { val deq = Wire(new DecoupledIO(chiselTypeOf(enq.bits))) deq.valid := enq.valid @@ -293,7 +304,7 @@ object Queue enq.ready := deq.ready deq } else { - val q = Module(new Queue(chiselTypeOf(enq.bits), entries, pipe, flow)) + val q = Module(new Queue(chiselTypeOf(enq.bits), entries, pipe, flow, useSyncReadMem)) q.io.enq.valid := enq.valid // not using <> so that override is allowed q.io.enq.bits := enq.bits enq.ready := q.io.enq.ready @@ -311,8 +322,9 @@ object Queue enq: ReadyValidIO[T], entries: Int = 2, pipe: Boolean = false, - flow: Boolean = false): IrrevocableIO[T] = { - val deq = apply(enq, entries, pipe, flow) + flow: Boolean = false, + useSyncReadMem: Boolean = false): IrrevocableIO[T] = { + val deq = apply(enq, entries, pipe, flow, useSyncReadMem) require(entries > 0, "Zero-entry queues don't guarantee Irrevocability") val irr = Wire(new IrrevocableIO(chiselTypeOf(deq.bits))) irr.bits := deq.bits diff --git a/src/test/scala/chiselTests/QueueSpec.scala b/src/test/scala/chiselTests/QueueSpec.scala index 9dc7f120..3555a13c 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) extends BasicTester { - val q = Module(new Queue(UInt(bitWidth.W), queueDepth)) +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)) val elems = VecInit(elements.map { _.asUInt() }) @@ -34,8 +34,8 @@ class ThingsPassThroughTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int } } -class QueueReasonableReadyValid(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester { - val q = Module(new Queue(UInt(bitWidth.W), queueDepth)) +class QueueReasonableReadyValid(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester { + val q = Module(new Queue(UInt(bitWidth.W), queueDepth, useSyncReadMem = useSyncReadMem)) val elems = VecInit(elements.map { _.asUInt() }) @@ -62,8 +62,8 @@ class QueueReasonableReadyValid(elements: Seq[Int], queueDepth: Int, bitWidth: I } } -class CountIsCorrectTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester { - val q = Module(new Queue(UInt(bitWidth.W), queueDepth)) +class CountIsCorrectTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester { + val q = Module(new Queue(UInt(bitWidth.W), queueDepth, useSyncReadMem = useSyncReadMem)) val elems = VecInit(elements.map { _.asUInt(bitWidth.W) }) @@ -89,8 +89,8 @@ class CountIsCorrectTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, t } } -class QueueSinglePipeTester(elements: Seq[Int], bitWidth: Int, tap: Int) extends BasicTester { - val q = Module(new Queue(UInt(bitWidth.W), 1, pipe = true)) +class QueueSinglePipeTester(elements: Seq[Int], bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester { + val q = Module(new Queue(UInt(bitWidth.W), 1, pipe = true, useSyncReadMem = useSyncReadMem)) val elems = VecInit(elements.map { _.asUInt(bitWidth.W) }) @@ -115,8 +115,8 @@ class QueueSinglePipeTester(elements: Seq[Int], bitWidth: Int, tap: Int) extends } } -class QueuePipeTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester { - val q = Module(new Queue(UInt(bitWidth.W), queueDepth, pipe = true)) +class QueuePipeTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester { + val q = Module(new Queue(UInt(bitWidth.W), queueDepth, pipe = true, useSyncReadMem = useSyncReadMem)) val elems = VecInit(elements.map { _.asUInt(bitWidth.W) }) @@ -141,8 +141,8 @@ class QueuePipeTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: I } } -class QueueFlowTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester { - val q = Module(new Queue(UInt(bitWidth.W), queueDepth, flow = true)) +class QueueFlowTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester { + val q = Module(new Queue(UInt(bitWidth.W), queueDepth, flow = true, useSyncReadMem = useSyncReadMem)) val elems = VecInit(elements.map { _.asUInt() }) @@ -169,9 +169,9 @@ class QueueFlowTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: I } } -class QueueFactoryTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester { +class QueueFactoryTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester { val enq = Wire(Decoupled(UInt(bitWidth.W))) - val deq = Queue(enq, queueDepth) + val deq = Queue(enq, queueDepth, useSyncReadMem = useSyncReadMem) val elems = VecInit(elements.map { _.asUInt() @@ -202,70 +202,70 @@ class QueueSpec extends ChiselPropSpec { implicit val noShrinkInt = Shrink[Int](_ => Stream.empty) property("Queue should have things pass through") { - forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) => + 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) + new ThingsPassThroughTester(se._2, depth, se._1, tap, isSync) } } } } property("Queue should have reasonable ready/valid") { - forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) => + 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 QueueReasonableReadyValid(se._2, depth, se._1, tap) + new QueueReasonableReadyValid(se._2, depth, se._1, tap, isSync) } } } } property("Queue should have correct count") { - forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) => + 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 CountIsCorrectTester(se._2, depth, se._1, tap) + new CountIsCorrectTester(se._2, depth, se._1, tap, isSync) } } } } property("Queue pipe should work for 1-element queues") { - forAll(safeUIntN(20), Gen.choose(0, 15)) { (se, tap) => + forAll(safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (se, tap, isSync) => whenever(se._1 >= 1 && se._2.nonEmpty) { assertTesterPasses { - new QueueSinglePipeTester(se._2, se._1, tap) + new QueueSinglePipeTester(se._2, se._1, tap, isSync) } } } } property("Queue pipe should work for more general queues") { - forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) => + 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 QueuePipeTester(se._2, depth, se._1, tap) + new QueuePipeTester(se._2, depth, se._1, tap, isSync) } } } } property("Queue flow should work") { - forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) => + 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 QueueFlowTester(se._2, depth, se._1, tap) + new QueueFlowTester(se._2, depth, se._1, tap, isSync) } } } } property("Queue companion object factory method should work") { - forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) => + forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) => whenever(se._1 >= 1 && se._2.nonEmpty) { assertTesterPasses { - new QueueFactoryTester(se._2, depth, se._1, tap) + new QueueFactoryTester(se._2, depth, se._1, tap, isSync) } } } -- cgit v1.2.3