Split out CoalescingUnitImp
This commit is contained in:
@@ -29,13 +29,18 @@ class CoalescingUnit(numLanes: Int = 1)(implicit p: Parameters)
|
|||||||
sourceId = IdRange(0, numInflightCoalRequests)
|
sourceId = IdRange(0, numInflightCoalRequests)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
protected val coalescerNode = TLClientNode(
|
val coalescerNode = TLClientNode(
|
||||||
Seq(TLMasterPortParameters.v1(coalParam))
|
Seq(TLMasterPortParameters.v1(coalParam))
|
||||||
)
|
)
|
||||||
|
|
||||||
// Connect master node as the first inward edge of the IdentityNode
|
// Connect master node as the first inward edge of the IdentityNode
|
||||||
node :=* coalescerNode
|
node :=* coalescerNode
|
||||||
|
|
||||||
|
lazy val module = new CoalescingUnitImp(this, numLanes)
|
||||||
|
}
|
||||||
|
|
||||||
|
class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int)
|
||||||
|
extends LazyModuleImp(outer) {
|
||||||
class ReqQueueEntry(val sourceWidth: Int, val addressWidth: Int)
|
class ReqQueueEntry(val sourceWidth: Int, val addressWidth: Int)
|
||||||
extends Bundle {
|
extends Bundle {
|
||||||
val source = UInt(sourceWidth.W)
|
val source = UInt(sourceWidth.W)
|
||||||
@@ -47,208 +52,209 @@ class CoalescingUnit(numLanes: Int = 1)(implicit p: Parameters)
|
|||||||
val data = UInt(64.W /* FIXME hardcoded */ ) // read data
|
val data = UInt(64.W /* FIXME hardcoded */ ) // read data
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy val module = new Impl
|
// node.in(0) is from coalescer TL master node; 1~N are from cores
|
||||||
class Impl extends LazyModuleImp(this) {
|
// assert(node.in.length >= 2)
|
||||||
// node.in(0) is from coalescer TL master node; 1~N are from cores
|
val sourceWidth = outer.node.in(1)._1.params.sourceBits
|
||||||
// assert(node.in.length >= 2)
|
val addressWidth = outer.node.in(1)._1.params.addressBits
|
||||||
val sourceWidth = node.in(1)._1.params.sourceBits
|
val reqQueueEntryT = new ReqQueueEntry(sourceWidth, addressWidth)
|
||||||
val addressWidth = node.in(1)._1.params.addressBits
|
val reqQueues = Seq.tabulate(numLanes) { _ =>
|
||||||
val reqQueueEntryT = new ReqQueueEntry(sourceWidth, addressWidth)
|
Module(
|
||||||
val reqQueues = Seq.tabulate(numLanes) { _ =>
|
new ShiftQueue(reqQueueEntryT, 4 /* FIXME hardcoded */ )
|
||||||
Module(
|
|
||||||
new ShiftQueue(reqQueueEntryT, 4 /* FIXME hardcoded */ )
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val respQueueEntryT = new RespQueueEntry(sourceWidth)
|
|
||||||
val respQueues = Seq.tabulate(numLanes) { _ =>
|
|
||||||
Module(
|
|
||||||
new ShiftQueue(respQueueEntryT, 8 /* FIXME hardcoded */ )
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Per-lane request and response queues
|
|
||||||
//
|
|
||||||
// Override IdentityNode implementation so that we wire node output to the
|
|
||||||
// queue output, instead of directly passing through node input.
|
|
||||||
// See IdentityNode definition in `diplomacy/Nodes.scala`.
|
|
||||||
(node.in zip node.out).zipWithIndex.foreach {
|
|
||||||
case (((_, edgeIn), _), 0) =>
|
|
||||||
// No need to do anything on the edge from coalescerNode
|
|
||||||
assert(
|
|
||||||
edgeIn.master.masters(0).name == "CoalescerNode",
|
|
||||||
"First edge is not connected to the coalescer master node"
|
|
||||||
)
|
|
||||||
case (((tlIn, edgeIn), (tlOut, edgeOut)), i) =>
|
|
||||||
// Request queue
|
|
||||||
//
|
|
||||||
val reqQueue = reqQueues(i - 1)
|
|
||||||
val req = Wire(reqQueueEntryT)
|
|
||||||
req.source := tlIn.a.bits.source
|
|
||||||
req.address := tlIn.a.bits.address
|
|
||||||
req.data := tlIn.a.bits.data
|
|
||||||
|
|
||||||
println(s"============ req.source width=${req.source.widthOption.get}")
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
tlOut.a.valid := reqQueue.io.deq.valid
|
|
||||||
val reqHead = reqQueue.io.deq.bits
|
|
||||||
// FIXME: generate Get or Put according to read/write
|
|
||||||
val (reqLegal, reqBits) = edgeOut.Get(
|
|
||||||
fromSource = reqHead.source,
|
|
||||||
// `toAddress` should be aligned to 2**lgSize
|
|
||||||
toAddress = reqHead.address,
|
|
||||||
lgSize = 0.U
|
|
||||||
)
|
|
||||||
assert(reqLegal, "unhandled illegal TL req gen")
|
|
||||||
tlOut.a.bits := reqBits
|
|
||||||
|
|
||||||
// Response queue
|
|
||||||
//
|
|
||||||
// This queue will serialize non-coalesced responses along with
|
|
||||||
// coalesced responses and serve them back to the core side.
|
|
||||||
val respQueue = respQueues(i - 1)
|
|
||||||
val resp = Wire(respQueueEntryT)
|
|
||||||
resp.source := tlOut.d.bits.source
|
|
||||||
resp.data := tlOut.d.bits.data
|
|
||||||
|
|
||||||
respQueue.io.enq.valid := tlOut.d.valid
|
|
||||||
respQueue.io.enq.bits := resp
|
|
||||||
// TODO: deq.ready should respect upstream ready
|
|
||||||
respQueue.io.deq.ready := true.B
|
|
||||||
|
|
||||||
tlIn.d.valid := respQueue.io.deq.valid
|
|
||||||
val respHead = respQueue.io.deq.bits
|
|
||||||
val respBits = edgeIn.AccessAck(
|
|
||||||
toSource = respHead.source,
|
|
||||||
lgSize = 0.U,
|
|
||||||
data = respHead.data
|
|
||||||
)
|
|
||||||
tlIn.d.bits := respBits
|
|
||||||
|
|
||||||
// tlIn.d <> tlOut.d
|
|
||||||
|
|
||||||
// Debug only
|
|
||||||
val inflightCounter = RegInit(UInt(32.W), 0.U)
|
|
||||||
when(tlOut.a.valid) {
|
|
||||||
// don't inc/dec on simultaneous req/resp
|
|
||||||
when(!tlOut.d.valid) {
|
|
||||||
inflightCounter := inflightCounter + 1.U
|
|
||||||
}
|
|
||||||
}.elsewhen(tlOut.d.valid) {
|
|
||||||
inflightCounter := inflightCounter - 1.U
|
|
||||||
}
|
|
||||||
|
|
||||||
dontTouch(inflightCounter)
|
|
||||||
dontTouch(tlIn.a)
|
|
||||||
dontTouch(tlIn.d)
|
|
||||||
dontTouch(tlOut.a)
|
|
||||||
dontTouch(tlOut.d)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate coalesced requests
|
|
||||||
// FIXME: currently generating bogus coalesced requests
|
|
||||||
val coalSourceId = RegInit(0.U(2.W /* FIXME hardcoded */ ))
|
|
||||||
coalSourceId := coalSourceId + 1.U
|
|
||||||
|
|
||||||
val (tlCoal, edgeCoal) = coalescerNode.out(0)
|
|
||||||
val coalReqAddress = Wire(UInt(tlCoal.params.addressBits.W))
|
|
||||||
// TODO: bogus address
|
|
||||||
coalReqAddress := (0xabcd.U + coalSourceId) << 4
|
|
||||||
val coalReqValid = Wire(Bool())
|
|
||||||
// FIXME: copy lane 1's valid signal. This is completely bogus
|
|
||||||
coalReqValid := node.in(1)._1.a.valid
|
|
||||||
|
|
||||||
val (legal, bits) = edgeCoal.Get(
|
|
||||||
fromSource = coalSourceId,
|
|
||||||
// `toAddress` should be aligned to 2**lgSize
|
|
||||||
toAddress = coalReqAddress,
|
|
||||||
// 64 bits = 8 bytes = 2**(3) bytes
|
|
||||||
lgSize = 3.U
|
|
||||||
)
|
)
|
||||||
assert(legal, "unhandled illegal TL req gen")
|
|
||||||
tlCoal.a.valid := coalReqValid
|
|
||||||
tlCoal.a.bits := bits
|
|
||||||
tlCoal.b.ready := true.B
|
|
||||||
tlCoal.c.valid := false.B
|
|
||||||
tlCoal.d.ready := true.B
|
|
||||||
tlCoal.e.valid := false.B
|
|
||||||
|
|
||||||
// Construct new entry for the inflight table
|
|
||||||
val inflightTable = Module(
|
|
||||||
new InflightCoalReqTable(numLanes, sourceWidth, numInflightCoalRequests)
|
|
||||||
)
|
|
||||||
val newEntry = Wire(inflightTable.entryT)
|
|
||||||
newEntry.respSourceId := coalSourceId
|
|
||||||
newEntry.lanes.foreach { l =>
|
|
||||||
l.valid := false.B
|
|
||||||
l.reqs.foreach { r =>
|
|
||||||
// TODO: this part needs the actual coalescing logic to work
|
|
||||||
r.valid := true.B
|
|
||||||
r.offset := 1.U
|
|
||||||
r.size := 2.U
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newEntry.lanes(0).valid := true.B
|
|
||||||
newEntry.lanes(2).valid := true.B
|
|
||||||
dontTouch(newEntry)
|
|
||||||
|
|
||||||
// Populate inflight coalesced request table
|
|
||||||
inflightTable.io.enq.valid := coalReqValid
|
|
||||||
inflightTable.io.enq.bits := newEntry
|
|
||||||
|
|
||||||
// Look up the table with incoming coalesced responses
|
|
||||||
inflightTable.io.lookup.ready := tlCoal.d.valid
|
|
||||||
inflightTable.io.lookupSourceId := tlCoal.d.bits.source
|
|
||||||
val coalRespData = Wire(UInt(tlCoal.params.dataBits.W))
|
|
||||||
coalRespData := tlCoal.d.bits.data
|
|
||||||
|
|
||||||
when(inflightTable.io.lookup.valid) {
|
|
||||||
val found = inflightTable.io.lookup.bits
|
|
||||||
found.lanes.zipWithIndex.foreach { case (l, i) =>
|
|
||||||
val respQueue = respQueues(i)
|
|
||||||
respQueue.io.enq.valid := l.valid
|
|
||||||
respQueue.io.enq.bits.source := 0.U // FIXME: only looking at 0th entry
|
|
||||||
// FIXME: disregard size enum for now
|
|
||||||
val sizeMask = (1.U << 4) - 1.U
|
|
||||||
val dataWidth = tlCoal.params.dataBits
|
|
||||||
// FIXME: handle multi-head input to the queue
|
|
||||||
respQueue.io.enq.bits.data := (coalRespData >> (dataWidth - 4)) & sizeMask
|
|
||||||
|
|
||||||
when(l.valid) {
|
|
||||||
when(l.reqs(0).valid) {
|
|
||||||
printf(s"lane ${i} req 0 is valid!\n")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(node.in zip node.out)(0) match {
|
|
||||||
case ((tlIn, edgeIn), (tlOut, _)) =>
|
|
||||||
assert(
|
|
||||||
edgeIn.master.masters.length == 1 &&
|
|
||||||
edgeIn.master.masters(0).name == "CoalescerNode",
|
|
||||||
"First edge is not connected to the coalescer master node"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: do we need to do anything here?
|
|
||||||
tlOut.a <> tlIn.a
|
|
||||||
tlIn.d <> tlOut.d
|
|
||||||
dontTouch(tlIn.d)
|
|
||||||
dontTouch(tlOut.d)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug
|
|
||||||
dontTouch(coalReqValid)
|
|
||||||
dontTouch(coalReqAddress)
|
|
||||||
dontTouch(coalRespData)
|
|
||||||
|
|
||||||
dontTouch(tlCoal.a)
|
|
||||||
dontTouch(tlCoal.d)
|
|
||||||
}
|
}
|
||||||
|
val respQueueEntryT = new RespQueueEntry(sourceWidth)
|
||||||
|
val respQueues = Seq.tabulate(numLanes) { _ =>
|
||||||
|
Module(
|
||||||
|
new ShiftQueue(respQueueEntryT, 8 /* FIXME hardcoded */ )
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per-lane request and response queues
|
||||||
|
//
|
||||||
|
// Override IdentityNode implementation so that we wire node output to the
|
||||||
|
// queue output, instead of directly passing through node input.
|
||||||
|
// See IdentityNode definition in `diplomacy/Nodes.scala`.
|
||||||
|
(outer.node.in zip outer.node.out).zipWithIndex.foreach {
|
||||||
|
case (((_, edgeIn), _), 0) =>
|
||||||
|
// No need to do anything on the edge from coalescerNode
|
||||||
|
assert(
|
||||||
|
edgeIn.master.masters(0).name == "CoalescerNode",
|
||||||
|
"First edge is not connected to the coalescer master node"
|
||||||
|
)
|
||||||
|
case (((tlIn, edgeIn), (tlOut, edgeOut)), i) =>
|
||||||
|
// Request queue
|
||||||
|
//
|
||||||
|
val reqQueue = reqQueues(i - 1)
|
||||||
|
val req = Wire(reqQueueEntryT)
|
||||||
|
req.source := tlIn.a.bits.source
|
||||||
|
req.address := tlIn.a.bits.address
|
||||||
|
req.data := tlIn.a.bits.data
|
||||||
|
|
||||||
|
println(s"============ req.source width=${req.source.widthOption.get}")
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
tlOut.a.valid := reqQueue.io.deq.valid
|
||||||
|
val reqHead = reqQueue.io.deq.bits
|
||||||
|
// FIXME: generate Get or Put according to read/write
|
||||||
|
val (reqLegal, reqBits) = edgeOut.Get(
|
||||||
|
fromSource = reqHead.source,
|
||||||
|
// `toAddress` should be aligned to 2**lgSize
|
||||||
|
toAddress = reqHead.address,
|
||||||
|
lgSize = 0.U
|
||||||
|
)
|
||||||
|
assert(reqLegal, "unhandled illegal TL req gen")
|
||||||
|
tlOut.a.bits := reqBits
|
||||||
|
|
||||||
|
// Response queue
|
||||||
|
//
|
||||||
|
// This queue will serialize non-coalesced responses along with
|
||||||
|
// coalesced responses and serve them back to the core side.
|
||||||
|
val respQueue = respQueues(i - 1)
|
||||||
|
val resp = Wire(respQueueEntryT)
|
||||||
|
resp.source := tlOut.d.bits.source
|
||||||
|
resp.data := tlOut.d.bits.data
|
||||||
|
|
||||||
|
respQueue.io.enq.valid := tlOut.d.valid
|
||||||
|
respQueue.io.enq.bits := resp
|
||||||
|
// TODO: deq.ready should respect upstream ready
|
||||||
|
respQueue.io.deq.ready := true.B
|
||||||
|
|
||||||
|
tlIn.d.valid := respQueue.io.deq.valid
|
||||||
|
val respHead = respQueue.io.deq.bits
|
||||||
|
val respBits = edgeIn.AccessAck(
|
||||||
|
toSource = respHead.source,
|
||||||
|
lgSize = 0.U,
|
||||||
|
data = respHead.data
|
||||||
|
)
|
||||||
|
tlIn.d.bits := respBits
|
||||||
|
|
||||||
|
// tlIn.d <> tlOut.d
|
||||||
|
|
||||||
|
// Debug only
|
||||||
|
val inflightCounter = RegInit(UInt(32.W), 0.U)
|
||||||
|
when(tlOut.a.valid) {
|
||||||
|
// don't inc/dec on simultaneous req/resp
|
||||||
|
when(!tlOut.d.valid) {
|
||||||
|
inflightCounter := inflightCounter + 1.U
|
||||||
|
}
|
||||||
|
}.elsewhen(tlOut.d.valid) {
|
||||||
|
inflightCounter := inflightCounter - 1.U
|
||||||
|
}
|
||||||
|
|
||||||
|
dontTouch(inflightCounter)
|
||||||
|
dontTouch(tlIn.a)
|
||||||
|
dontTouch(tlIn.d)
|
||||||
|
dontTouch(tlOut.a)
|
||||||
|
dontTouch(tlOut.d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate coalesced requests
|
||||||
|
// FIXME: currently generating bogus coalesced requests
|
||||||
|
val coalSourceId = RegInit(0.U(2.W /* FIXME hardcoded */ ))
|
||||||
|
coalSourceId := coalSourceId + 1.U
|
||||||
|
|
||||||
|
val (tlCoal, edgeCoal) = outer.coalescerNode.out(0)
|
||||||
|
val coalReqAddress = Wire(UInt(tlCoal.params.addressBits.W))
|
||||||
|
// TODO: bogus address
|
||||||
|
coalReqAddress := (0xabcd.U + coalSourceId) << 4
|
||||||
|
val coalReqValid = Wire(Bool())
|
||||||
|
// FIXME: copy lane 1's valid signal. This is completely bogus
|
||||||
|
coalReqValid := outer.node.in(1)._1.a.valid
|
||||||
|
|
||||||
|
val (legal, bits) = edgeCoal.Get(
|
||||||
|
fromSource = coalSourceId,
|
||||||
|
// `toAddress` should be aligned to 2**lgSize
|
||||||
|
toAddress = coalReqAddress,
|
||||||
|
// 64 bits = 8 bytes = 2**(3) bytes
|
||||||
|
lgSize = 3.U
|
||||||
|
)
|
||||||
|
assert(legal, "unhandled illegal TL req gen")
|
||||||
|
tlCoal.a.valid := coalReqValid
|
||||||
|
tlCoal.a.bits := bits
|
||||||
|
tlCoal.b.ready := true.B
|
||||||
|
tlCoal.c.valid := false.B
|
||||||
|
tlCoal.d.ready := true.B
|
||||||
|
tlCoal.e.valid := false.B
|
||||||
|
|
||||||
|
// Construct new entry for the inflight table
|
||||||
|
val inflightTable = Module(
|
||||||
|
new InflightCoalReqTable(
|
||||||
|
numLanes,
|
||||||
|
sourceWidth,
|
||||||
|
outer.numInflightCoalRequests
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val newEntry = Wire(inflightTable.entryT)
|
||||||
|
newEntry.respSourceId := coalSourceId
|
||||||
|
newEntry.lanes.foreach { l =>
|
||||||
|
l.valid := false.B
|
||||||
|
l.reqs.foreach { r =>
|
||||||
|
// TODO: this part needs the actual coalescing logic to work
|
||||||
|
r.valid := true.B
|
||||||
|
r.offset := 1.U
|
||||||
|
r.size := 2.U
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newEntry.lanes(0).valid := true.B
|
||||||
|
newEntry.lanes(2).valid := true.B
|
||||||
|
dontTouch(newEntry)
|
||||||
|
|
||||||
|
// Populate inflight coalesced request table
|
||||||
|
inflightTable.io.enq.valid := coalReqValid
|
||||||
|
inflightTable.io.enq.bits := newEntry
|
||||||
|
|
||||||
|
// Look up the table with incoming coalesced responses
|
||||||
|
inflightTable.io.lookup.ready := tlCoal.d.valid
|
||||||
|
inflightTable.io.lookupSourceId := tlCoal.d.bits.source
|
||||||
|
val coalRespData = Wire(UInt(tlCoal.params.dataBits.W))
|
||||||
|
coalRespData := tlCoal.d.bits.data
|
||||||
|
|
||||||
|
when(inflightTable.io.lookup.valid) {
|
||||||
|
val found = inflightTable.io.lookup.bits
|
||||||
|
found.lanes.zipWithIndex.foreach { case (l, i) =>
|
||||||
|
val respQueue = respQueues(i)
|
||||||
|
respQueue.io.enq.valid := l.valid
|
||||||
|
respQueue.io.enq.bits.source := 0.U // FIXME: only looking at 0th entry
|
||||||
|
// FIXME: disregard size enum for now
|
||||||
|
val sizeMask = (1.U << 4) - 1.U
|
||||||
|
val dataWidth = tlCoal.params.dataBits
|
||||||
|
// FIXME: handle multi-head input to the queue
|
||||||
|
respQueue.io.enq.bits.data := (coalRespData >> (dataWidth - 4)) & sizeMask
|
||||||
|
|
||||||
|
when(l.valid) {
|
||||||
|
when(l.reqs(0).valid) {
|
||||||
|
printf(s"lane ${i} req 0 is valid!\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(outer.node.in zip outer.node.out)(0) match {
|
||||||
|
case ((tlIn, edgeIn), (tlOut, _)) =>
|
||||||
|
assert(
|
||||||
|
edgeIn.master.masters.length == 1 &&
|
||||||
|
edgeIn.master.masters(0).name == "CoalescerNode",
|
||||||
|
"First edge is not connected to the coalescer master node"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: do we need to do anything here?
|
||||||
|
tlOut.a <> tlIn.a
|
||||||
|
tlIn.d <> tlOut.d
|
||||||
|
dontTouch(tlIn.d)
|
||||||
|
dontTouch(tlOut.d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug
|
||||||
|
dontTouch(coalReqValid)
|
||||||
|
dontTouch(coalReqAddress)
|
||||||
|
dontTouch(coalRespData)
|
||||||
|
|
||||||
|
dontTouch(tlCoal.a)
|
||||||
|
dontTouch(tlCoal.d)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InflightCoalReqTable is a reservation station-like structure that records
|
// InflightCoalReqTable is a reservation station-like structure that records
|
||||||
|
|||||||
Reference in New Issue
Block a user