Implement invalidation for the queue

This commit is contained in:
Hansung Kim
2023-03-31 19:25:25 -07:00
parent a0d75530cb
commit f44dfc8d5a
2 changed files with 78 additions and 9 deletions

View File

@@ -459,11 +459,12 @@ class InflightCoalReqTableEntry(
// Mostly copied from freechips.rocketchip.util.ShiftQueue, except that every
// queue entry and its valid signal are exposed as output IO.
// If `pipe` is true, support enqueueing to a full queue when also dequeueing.
// TODO: support invalidate and deadline
class CoalShiftQueue[T <: Data](
gen: T,
val entries: Int,
pipe: Boolean = false,
pipe: Boolean = true,
flow: Boolean = false
) extends Module {
val io = IO(new QueueIO(gen, entries) {
@@ -487,23 +488,28 @@ class CoalShiftQueue[T <: Data](
private val used = RegInit(UInt(entries.W), 0.U)
private val elts = Reg(Vec(entries, gen))
for (i <- 0 until entries) {
def paddedValid(i: Int) = if (i == -1) true.B else if (i == entries) false.B else valid(i)
def paddedUsed(i: Int) = if (i == -1) true.B else if (i == entries) false.B else used(i)
def paddedValid(i: Int) = if (i == -1) true.B else if (i == entries) false.B else valid(i)
def paddedUsed(i: Int) = if (i == -1) true.B else if (i == entries) false.B else used(i)
def paddedValidAfterInvalidate(i: Int) =
if (i == -1) true.B
else if (i == entries) false.B
else Mux(io.invalidate(i), false.B, paddedValid(i))
for (i <- 0 until entries) {
val wdata = if (i == entries - 1) io.enq.bits else Mux(!used(i + 1), io.enq.bits, elts(i + 1))
val wen = Mux(
io.deq.ready,
paddedValid(i + 1) || io.enq.fire && ((i == 0 && !flow).B || used(i)),
(io.enq.fire && !paddedUsed(i + 1) && used(i)) || paddedValidAfterInvalidate(i + 1),
// enqueue to the first empty slot above the top
io.enq.fire && paddedUsed(i - 1) && !valid(i)
(io.enq.fire && paddedUsed(i - 1) && !used(i)) || !paddedValidAfterInvalidate(i)
)
when(wen) { elts(i) := wdata }
valid(i) := Mux(
io.deq.ready,
paddedValid(i + 1) || io.enq.fire && ((i == 0 && !flow).B || valid(i)),
io.enq.fire && paddedUsed(i - 1) || valid(i)
(io.enq.fire && !paddedUsed(i + 1) && used(i)) || paddedValidAfterInvalidate(i + 1),
// TODO: handle enqueueing to invalidated tail?
(io.enq.fire && paddedUsed(i - 1) && !used(i)) || paddedValidAfterInvalidate(i)
)
}
@@ -516,9 +522,10 @@ class CoalShiftQueue[T <: Data](
}
io.enq.ready := !valid(entries - 1)
io.deq.valid := valid(0)
io.deq.valid := paddedValidAfterInvalidate(0)
io.deq.bits := elts.head
assert(!flow, "flow-through is not implemented")
if (flow) {
when(io.enq.valid) { io.deq.valid := true.B }
when(!valid(0)) { io.deq.bits := io.enq.bits }

View File

@@ -1,6 +1,7 @@
import chisel3._
import chiseltest._
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest._
import freechips.rocketchip.tilelink._
import freechips.rocketchip.util.MultiPortQueue
@@ -82,6 +83,8 @@ class CoalShiftQueueTest extends AnyFlatSpec with ChiselScalatestTester {
it should "work when enqueing and dequeueing simultaneously" in {
test(new CoalShiftQueue(UInt(8.W), 4)) { c =>
c.io.invalidate.poke(0.U)
// prepare
c.io.deq.ready.poke(false.B)
c.io.enq.ready.expect(true.B)
@@ -108,6 +111,65 @@ class CoalShiftQueueTest extends AnyFlatSpec with ChiselScalatestTester {
c.io.deq.valid.expect(false.B)
}
}
it should "invalidate entry being dequeued combinationally" in {
test(new CoalShiftQueue(UInt(8.W), 4)) { c =>
c.io.invalidate.poke(0.U)
// prepare
c.io.deq.ready.poke(false.B)
c.io.enq.ready.expect(true.B)
c.io.enq.valid.poke(true.B)
c.io.enq.bits.poke(0x12.U)
c.clock.step()
c.io.deq.ready.poke(false.B)
c.io.enq.ready.expect(true.B)
c.io.enq.valid.poke(true.B)
c.io.enq.bits.poke(0x34.U)
c.clock.step()
c.io.enq.valid.poke(false.B)
// invalidate should work for the entry just being dequeued at the same
// cycle
c.io.invalidate.poke(0x1.U)
c.io.deq.ready.poke(true.B)
c.io.deq.valid.expect(false.B)
c.clock.step()
// rest are unchanged
c.io.invalidate.poke(0.U)
c.io.deq.ready.poke(true.B)
c.io.deq.valid.expect(true.B)
c.io.deq.bits.expect(0x34.U)
}
}
it should "dequeue invalidated entries by itself" in {
test(new CoalShiftQueue(UInt(8.W), 4)) { c =>
c.io.invalidate.poke(0.U)
// prepare
c.io.deq.ready.poke(false.B)
c.io.enq.ready.expect(true.B)
c.io.enq.valid.poke(true.B)
c.io.enq.bits.poke(0x12.U)
c.clock.step()
c.io.deq.ready.poke(false.B)
c.io.enq.ready.expect(true.B)
c.io.enq.valid.poke(true.B)
c.io.enq.bits.poke(0x34.U)
c.clock.step()
c.io.enq.valid.poke(false.B)
c.io.invalidate.poke(0x1.U)
c.clock.step()
c.io.deq.ready.poke(false.B)
// 0x12 should be dequeued
c.clock.step()
c.io.deq.ready.poke(true.B)
c.io.deq.valid.expect(true.B)
c.io.deq.bits.expect(0x34.U)
}
}
}
class UncoalescingUnitTest extends AnyFlatSpec with ChiselScalatestTester {