1
0
Fork 0
rocket-chip/src/main/scala/util/PositionalMultiQueue.scala

100 lines
3.3 KiB
Scala

// See LICENSE.SiFive for license details.
package freechips.rocketchip.util
import Chisel._
case class PositionalMultiQueueParameters[T <: Data](gen: T, positions: Int, ways: Int)
class PositionalMultiQueueEntry[T <: Data](params: PositionalMultiQueueParameters[T])
extends GenericParameterizedBundle(params)
{
val data = params.gen.asOutput
val pos = UInt(width = log2Up(params.positions))
}
class PositionalMultiQueuePush[T <: Data](params: PositionalMultiQueueParameters[T])
extends PositionalMultiQueueEntry(params)
{
val way = UInt(width = log2Up(params.ways))
}
/* A PositionalMultiQueue is like a normal Queue, except that it stores (position, value).
* When you pop it, you get back the oldest (position, value) pushed into it.
* >>>>> You must guarantee that you never enque to an occupied position. <<<<<
* Unlike a normal Queue, a PositionalMultiQueue has multiple deque ports (ways).
* You select which way will deque a given (position, value) when you enque it.
* If combinational, deque ports become valid on the same cycle as the enque.
*/
class PositionalMultiQueue[T <: Data](params: PositionalMultiQueueParameters[T], combinational: Boolean) extends Module
{
val io = new Bundle {
val enq = Valid(new PositionalMultiQueuePush(params)).flip
val deq = Vec(params.ways, Decoupled(new PositionalMultiQueueEntry(params)))
}
val empty = RegInit(Vec.fill(params.ways) { Bool(true) })
val head = Reg(Vec(params.ways, UInt(width = log2Up(params.positions))))
val tail = Reg(Vec(params.ways, UInt(width = log2Up(params.positions))))
val next = Mem(params.positions, UInt(width = log2Up(params.positions)))
val data = Mem(params.positions, params.gen)
// optimized away for synthesis; used to confirm invariant
val guard = RegInit(UInt(0, width = params.positions))
when (io.enq.fire()) {
data(io.enq.bits.pos) := io.enq.bits.data
// ensure the user never stores to the same position twice
assert (!guard(io.enq.bits.pos))
when (!empty(io.enq.bits.way)) {
next(tail(io.enq.bits.way)) := io.enq.bits.pos
}
}
val setGuard = io.enq.fire() << io.enq.bits.pos
val deq = Wire(io.deq)
io.deq <> deq
val waySelect = UIntToOH(io.enq.bits.way, params.ways)
var clrGuard = UInt(0)
for (i <- 0 until params.ways) {
val enq = io.enq.fire() && waySelect(i)
val last = head(i) === tail(i)
when (enq) {
tail(i) := io.enq.bits.pos
when (empty(i)) {
head(i) := io.enq.bits.pos
}
}
if (combinational) {
deq(i).valid := !empty(i) || enq
deq(i).bits.pos := Mux(empty(i), io.enq.bits.pos, head(i))
deq(i).bits.data := Mux(empty(i), io.enq.bits.data, data(head(i)))
} else {
deq(i).valid := !empty(i)
deq(i).bits.pos := head(i)
deq(i).bits.data := data(head(i))
}
when (deq(i).fire()) {
head(i) := Mux(last, io.enq.bits.pos, next(head(i)))
}
clrGuard = clrGuard | (deq(i).fire() << deq(i).bits.pos)
when (enq =/= deq(i).fire()) {
empty(i) := deq(i).fire() && last
}
}
guard := (guard | setGuard) & ~clrGuard
}
object PositionalMultiQueue
{
def apply[T <: Data](gen: T, positions: Int, ways: Int = 1, combinational: Boolean = true) = {
Module(new PositionalMultiQueue(PositionalMultiQueueParameters(gen, positions, ways), combinational))
}
}