Use used mask in CoalShiftQueue

Easier to use than wptr when enqueuing & dequeueing at the same time.
This commit is contained in:
Hansung Kim
2023-03-31 17:07:59 -07:00
parent 303c43a5e2
commit a0d75530cb
2 changed files with 56 additions and 15 deletions

View File

@@ -50,17 +50,18 @@ class RespQueueEntry(val sourceWidth: Int, val dataWidthInBits: Int) extends Bun
}
class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModuleImp(outer) {
// Make sure IdentityNode is connected to an upstream node, not just the
// coalescer TL master node
assert(outer.node.in.length >= 2)
val wordSize = 4
// node.in(0) is from coalescer TL master node; 1~N are from cores
// assert(node.in.length >= 2)
val reqQueueDepth = 4 // FIXME test
val sourceWidth = outer.node.in(1)._1.params.sourceBits
val addressWidth = outer.node.in(1)._1.params.addressBits
val reqQueueEntryT = new ReqQueueEntry(sourceWidth, addressWidth)
val reqQueues = Seq.tabulate(numLanes) { _ =>
Module(
new CoalShiftQueue(reqQueueEntryT, 4 /* FIXME hardcoded */ )
)
Module(new CoalShiftQueue(reqQueueEntryT, reqQueueDepth))
}
// The maximum number of requests from a single lane that can go into a
@@ -458,6 +459,7 @@ class InflightCoalReqTableEntry(
// Mostly copied from freechips.rocketchip.util.ShiftQueue, except that every
// queue entry and its valid signal are exposed as output IO.
// TODO: support invalidate and deadline
class CoalShiftQueue[T <: Data](
gen: T,
val entries: Int,
@@ -471,36 +473,46 @@ class CoalShiftQueue[T <: Data](
})
private val valid = RegInit(VecInit(Seq.fill(entries) { false.B }))
// Need to maintain a wptr because we can't simply tell where the queue tail
// is by just looking for invalid slots, because there may be holes in the middle
private val wptr = RegInit(UInt(entries.W), 1.U)
// "Used" flag is 1 for every entry between the current queue head and tail,
// even if that entry has been invalidated:
//
// used: 000011111
// valid: 000011011
// │ │ └─ head
// │ └────invalidated
// └──────tail
//
// Need this because we can't tell where to enqueue simply by looking at the
// valid bits.
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)
// val wdata = if (i == entries - 1) io.enq.bits else Mux(valid(i + 1), elts(i + 1), io.enq.bits)
val wdata = if (i == entries - 1) io.enq.bits else Mux(wptr(i), 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(
io.deq.ready,
paddedValid(i + 1) || io.enq.fire && ((i == 0 && !flow).B || valid(i)),
io.enq.fire && paddedValid(i - 1) && !valid(i)
paddedValid(i + 1) || io.enq.fire && ((i == 0 && !flow).B || used(i)),
// enqueue to the first empty slot above the top
io.enq.fire && paddedUsed(i - 1) && !valid(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 && paddedValid(i - 1) || valid(i)
io.enq.fire && paddedUsed(i - 1) || valid(i)
)
}
when(io.enq.fire) {
when(!io.deq.fire) {
wptr := wptr << 1.U
used := (used << 1.U) | 1.U
}
}.elsewhen(io.deq.fire) {
wptr := wptr >> 1.U
used := used >> 1.U
}
io.enq.ready := !valid(entries - 1)

View File

@@ -79,6 +79,35 @@ class CoalShiftQueueTest extends AnyFlatSpec with ChiselScalatestTester {
c.io.deq.valid.expect(false.B)
}
}
it should "work when enqueing and dequeueing simultaneously" in {
test(new CoalShiftQueue(UInt(8.W), 4)) { c =>
// 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()
// enqueue and dequeue simultaneously
c.io.deq.ready.poke(true.B)
c.io.enq.ready.expect(true.B)
c.io.enq.valid.poke(true.B)
c.io.enq.bits.poke(0x34.U)
c.io.deq.valid.expect(true.B)
c.io.deq.bits.expect(0x12.U)
c.clock.step()
// dequeueing back-to-back should work without any holes in the middle
c.io.deq.ready.poke(true.B)
c.io.enq.valid.poke(false.B)
c.io.deq.valid.expect(true.B)
c.io.deq.bits.expect(0x34.U)
c.clock.step()
// make sure is empty
c.io.deq.ready.poke(true.B)
c.io.enq.valid.poke(false.B)
c.io.deq.valid.expect(false.B)
}
}
}
class UncoalescingUnitTest extends AnyFlatSpec with ChiselScalatestTester {