WIP coalescer restructure

This commit is contained in:
Richard Yan
2023-04-19 01:03:30 -07:00
parent 8e5254292b
commit 8715bede37

View File

@@ -10,6 +10,22 @@ import freechips.rocketchip.diplomacy._
import freechips.rocketchip.util.MultiPortQueue
import freechips.rocketchip.unittest._
import org.chipsalliance.tilelink.OpCode
object CoalescerConsts {
val MAX_SIZE = 6 // maximum burst size (64 bytes)
val DEPTH = 1 // request window per lane
val WAIT_TIMEOUT = 8 // max cycles to wait before forced fifo dequeue, per lane
val ADDR_WIDTH = 32 // assume <= 32
val DATA_BUS_SIZE = 4 // 2^4=16 bytes, 128 bit bus
val NUM_LANES = 4
// val WATERMARK = 2 // minimum buffer occupancy to start coalescing
val WORD_SIZE = 4 // 32-bit system
val WORD_WIDTH = 2 // log(WORD_SIZE)
val NUM_OLD_IDS = 8 // num of outstanding requests per lane, from processor
val NUM_NEW_IDS = 4 // num of outstanding coalesced requests
}
class CoalescingUnit(numLanes: Int = 1)(implicit p: Parameters) extends LazyModule {
// Identity node that captures the incoming TL requests and passes them
// through the other end, dropping coalesced requests. This node is what
@@ -18,7 +34,7 @@ class CoalescingUnit(numLanes: Int = 1)(implicit p: Parameters) extends LazyModu
// Number of maximum in-flight coalesced requests. The upper bound of this
// value would be the sourceId range of a single lane.
val numInflightCoalRequests = 4
val numInflightCoalRequests = CoalescerConsts.NUM_NEW_IDS
// Master node that actually generates coalesced requests.
protected val coalParam = Seq(
@@ -37,37 +53,74 @@ class CoalescingUnit(numLanes: Int = 1)(implicit p: Parameters) extends LazyModu
lazy val module = new CoalescingUnitImp(this, numLanes)
}
class ReqQueueEntry(val sourceWidth: Int, val addressWidth: Int) extends Bundle {
val source = UInt(sourceWidth.W)
class ReqQueueEntry(sourceWidth: Int, sizeWidth: Int, addressWidth: Int, maxSize: Int) extends Bundle {
val op = UInt(1.W) // 0=READ 1=WRITE
val address = UInt(addressWidth.W)
val data = UInt(64.W /* FIXME hardcoded */ ) // write data
val size = UInt(sizeWidth.W)
val source = UInt(sourceWidth.W)
val mask = UInt((1 << maxSize).W) // write only
val data = UInt((8 * (1 << maxSize)).W) // write only
}
class RespQueueEntry(val sourceWidth: Int, val dataWidthInBits: Int) extends Bundle {
class RespQueueEntry(sourceWidth: Int, sizeWidth: Int, maxSize: Int) extends Bundle {
val op = UInt(1.W) // 0=READ 1=WRITE
val size = UInt(sizeWidth.W)
val source = UInt(sourceWidth.W)
val data = UInt(dataWidthInBits.W) // read data
val data = UInt((8 * (1 << maxSize)).W) // read only
val error = Bool()
}
class MonoCoalescer(size: Int, window: Seq[CoalShiftQueue]) extends Module {
// constructor: size, window
// inputs: none
// outputs: leader idx, base addr, match OH, match count, coverage hits
val io = IO(new Bundle {
val leader_idx = Output(UInt(log2Ceil(CoalescerConsts.NUM_LANES).W)),
val base_addr = Output(UInt(CoalescerConsts.ADDR_WIDTH.W)),
val match_oh = Output(Vec(NUM_LANES, UInt(CoalescerConsts.DEPTH.W))),
val coverage_hits = Output(UInt((1 << CoalescerConsts.MAX_SIZE).W))
})
// def can_match(req0: ReqQueueEntry, req1: ReqQueueEntry): Bool
// combinational logic to drive output from window contents
}
class MultiCoalescer(sizes: Seq[Int], window: Seq[CoalShiftQueue], reqQueueEntryT: ReqQueueEntry) extends Module {
// constructor: sizes, window: Seq[CoalShiftQueue]
// instantiate MonoCoalescers
// inputs: none
// outputs: out_req: Valid(ReqQueueEntry), invalidate: Valid(Seq[UInt(LOGDEPTH.W)])
val io = IO(new Bundle {
val out_req = Output(Valid(reqQueueEntryT.cloneType)),
val invalidate = Output(Valid(UInt(log2Ceil(LOGDEPTH.W)))),
})
}
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
val wordSize = WORD_SIZE
val reqQueueDepth = 4 // FIXME test
val respQueueDepth = 2 // FIXME test
val reqQueueDepth = DEPTH
val respQueueDepth = NUM_NEW_IDS
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 reqQueueEntryT = new ReqQueueEntry(sourceWidth, log2Ceil(MAX_SIZE), addressWidth)
val reqQueues = Seq.tabulate(numLanes) { _ =>
Module(new CoalShiftQueue(reqQueueEntryT, reqQueueDepth))
}
// The maximum number of requests from a single lane that can go into a
// coalesced request. Upper bound is 2**sourceWidth.
val numPerLaneReqs = 2
// coalesced request. Upper bound is min(DEPTH, 2**sourceWidth).
val numPerLaneReqs = DEPTH
val coalescer = Module(new MultiCoalescer(Seq(4, 5, 6), reqQueues, reqQueueEntryT))
val respQueueEntryT = new RespQueueEntry(sourceWidth, wordSize * 8)
val respQueues = Seq.tabulate(numLanes) { _ =>
@@ -96,8 +149,6 @@ class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModule
val respQueueNoncoalPort = 0
val respQueueCoalPortOffset = 1
// did coalescing succeed at all?
val coalReqValid = Wire(Bool())
// Per-lane request and response queues
//
@@ -105,7 +156,7 @@ class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModule
// queues between input and output edges to buffer requests and responses.
// See IdentityNode definition in `diplomacy/Nodes.scala`.
(outer.node.in zip outer.node.out).zipWithIndex.foreach {
case (((tlIn, edgeIn), (tlOut, _)), 0) =>
case (((tlIn, edgeIn), (tlOut, _)), 0) => // TODO: not necessarily 1 master edge
assert(
edgeIn.master.masters(0).name == "CoalescerNode",
"First edge is not connected to the coalescer master node"
@@ -121,24 +172,27 @@ class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModule
val lane = i - 1
val reqQueue = reqQueues(lane)
val req = Wire(reqQueueEntryT)
// **********
// CONNECTING IO
// **********
assert(~tlIn.a.valid || (tlIn.a.bits.opcode === OpCode.Get || tlIn.a.bits.opcode === OpCode.PutFullData ||
tlIn.a.bits.opcode === OpCode.PutPartialData), "Coalescer input has unsupported TL opcode");
req.op := tlIn.a.bits.opcode === OpCode.Get ? 0.U : 1.U
req.source := tlIn.a.bits.source
req.address := tlIn.a.bits.address
req.data := tlIn.a.bits.data
req.size := tlIn.a.bits.size
reqQueue.io.enq.valid := tlIn.a.valid
reqQueue.io.enq.bits := req
// TODO: deq.ready should respect downstream ready
reqQueue.io.deq.ready := true.B
reqQueue.io.invalidate := 0.U
reqQueue.io.invalidate.bits := 0.U // TODO
reqQueue.io.invalidate.valid := false.B // TODO
printf(s"reqQueue(${lane}).count=%d\n", reqQueue.io.count)
// Invalidate coalesced requests
// FIXME: hardcoded lanes
// val invalidate = coalReqValid && (lane == 0 || lane == 2).B
val invalidate = coalReqValid
tlOut.a.valid := reqQueue.io.deq.valid && !invalidate
val reqHead = reqQueue.io.deq.bits
// FIXME: generate Get or Put according to read/write
val (reqLegal, reqBits) = edgeOut.Get(
@@ -146,9 +200,11 @@ class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModule
// `toAddress` should be aligned to 2**lgSize
toAddress = reqHead.address,
lgSize = 0.U
)
)
assert(reqLegal, "unhandled illegal TL req gen")
tlOut.a.bits := reqBits
tlOut.a.bits := reqBits // TODO: this is incorrect, this does not take iinto account of queue
tlOut.a.valid := reqQueue.io.deq.valid
// Response queue
//
@@ -492,10 +548,12 @@ class CoalShiftQueue[T <: Data](
pipe: Boolean = true,
flow: Boolean = false
) extends Module {
val io = IO(new QueueIO(gen, entries) {
val invalidate = Input(UInt(entries.W))
val mask = Output(UInt(entries.W))
val elts = Output(Vec(entries, gen))
val io = IO(new Bundle {
val queue = new QueueIO(gen, entries) {
val invalidate = Input(Valid(UInt(entries.W)))
val mask = Output(UInt(entries.W))
val elts = Output(Vec(entries, gen))
}
})
private val valid = RegInit(VecInit(Seq.fill(entries) { false.B }))