Implement invalidation for the queue
This commit is contained in:
@@ -459,11 +459,12 @@ class InflightCoalReqTableEntry(
|
|||||||
|
|
||||||
// Mostly copied from freechips.rocketchip.util.ShiftQueue, except that every
|
// Mostly copied from freechips.rocketchip.util.ShiftQueue, except that every
|
||||||
// queue entry and its valid signal are exposed as output IO.
|
// 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
|
// TODO: support invalidate and deadline
|
||||||
class CoalShiftQueue[T <: Data](
|
class CoalShiftQueue[T <: Data](
|
||||||
gen: T,
|
gen: T,
|
||||||
val entries: Int,
|
val entries: Int,
|
||||||
pipe: Boolean = false,
|
pipe: Boolean = true,
|
||||||
flow: Boolean = false
|
flow: Boolean = false
|
||||||
) extends Module {
|
) extends Module {
|
||||||
val io = IO(new QueueIO(gen, entries) {
|
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 used = RegInit(UInt(entries.W), 0.U)
|
||||||
private val elts = Reg(Vec(entries, gen))
|
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 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 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 wdata = if (i == entries - 1) io.enq.bits else Mux(!used(i + 1), io.enq.bits, elts(i + 1))
|
||||||
val wen = Mux(
|
val wen = Mux(
|
||||||
io.deq.ready,
|
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
|
// 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 }
|
when(wen) { elts(i) := wdata }
|
||||||
|
|
||||||
valid(i) := Mux(
|
valid(i) := Mux(
|
||||||
io.deq.ready,
|
io.deq.ready,
|
||||||
paddedValid(i + 1) || io.enq.fire && ((i == 0 && !flow).B || valid(i)),
|
(io.enq.fire && !paddedUsed(i + 1) && used(i)) || paddedValidAfterInvalidate(i + 1),
|
||||||
io.enq.fire && paddedUsed(i - 1) || valid(i)
|
// 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.enq.ready := !valid(entries - 1)
|
||||||
io.deq.valid := valid(0)
|
io.deq.valid := paddedValidAfterInvalidate(0)
|
||||||
io.deq.bits := elts.head
|
io.deq.bits := elts.head
|
||||||
|
|
||||||
|
assert(!flow, "flow-through is not implemented")
|
||||||
if (flow) {
|
if (flow) {
|
||||||
when(io.enq.valid) { io.deq.valid := true.B }
|
when(io.enq.valid) { io.deq.valid := true.B }
|
||||||
when(!valid(0)) { io.deq.bits := io.enq.bits }
|
when(!valid(0)) { io.deq.bits := io.enq.bits }
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import chisel3._
|
import chisel3._
|
||||||
import chiseltest._
|
import chiseltest._
|
||||||
import org.scalatest.flatspec.AnyFlatSpec
|
import org.scalatest.flatspec.AnyFlatSpec
|
||||||
|
import org.scalatest._
|
||||||
import freechips.rocketchip.tilelink._
|
import freechips.rocketchip.tilelink._
|
||||||
import freechips.rocketchip.util.MultiPortQueue
|
import freechips.rocketchip.util.MultiPortQueue
|
||||||
|
|
||||||
@@ -82,6 +83,8 @@ class CoalShiftQueueTest extends AnyFlatSpec with ChiselScalatestTester {
|
|||||||
|
|
||||||
it should "work when enqueing and dequeueing simultaneously" in {
|
it should "work when enqueing and dequeueing simultaneously" in {
|
||||||
test(new CoalShiftQueue(UInt(8.W), 4)) { c =>
|
test(new CoalShiftQueue(UInt(8.W), 4)) { c =>
|
||||||
|
c.io.invalidate.poke(0.U)
|
||||||
|
|
||||||
// prepare
|
// prepare
|
||||||
c.io.deq.ready.poke(false.B)
|
c.io.deq.ready.poke(false.B)
|
||||||
c.io.enq.ready.expect(true.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)
|
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 {
|
class UncoalescingUnitTest extends AnyFlatSpec with ChiselScalatestTester {
|
||||||
|
|||||||
Reference in New Issue
Block a user