Make uncoalescer a separate module for better testability
This commit is contained in:
@@ -38,19 +38,20 @@ class CoalescingUnit(numLanes: Int = 1)(implicit p: Parameters) extends LazyModu
|
|||||||
lazy val module = new CoalescingUnitImp(this, numLanes)
|
lazy val module = new CoalescingUnitImp(this, numLanes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ReqQueueEntry(val sourceWidth: Int, val addressWidth: Int) extends Bundle {
|
||||||
|
val source = UInt(sourceWidth.W)
|
||||||
|
val address = UInt(addressWidth.W)
|
||||||
|
val data = UInt(64.W /* FIXME hardcoded */ ) // write data
|
||||||
|
}
|
||||||
|
|
||||||
|
class RespQueueEntry(val sourceWidth: Int, val dataWidth: Int) extends Bundle {
|
||||||
|
val source = UInt(sourceWidth.W)
|
||||||
|
val data = UInt(dataWidth.W) // read data
|
||||||
|
}
|
||||||
|
|
||||||
class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModuleImp(outer) {
|
class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModuleImp(outer) {
|
||||||
val wordSize = 4
|
val wordSize = 4
|
||||||
|
|
||||||
class ReqQueueEntry(val sourceWidth: Int, val addressWidth: Int) extends Bundle {
|
|
||||||
val source = UInt(sourceWidth.W)
|
|
||||||
val address = UInt(addressWidth.W)
|
|
||||||
val data = UInt(64.W /* FIXME hardcoded */ ) // write data
|
|
||||||
}
|
|
||||||
class RespQueueEntry(val sourceWidth: Int) extends Bundle {
|
|
||||||
val source = UInt(sourceWidth.W)
|
|
||||||
val data = UInt(wordSize.W /* TODO double-word? */ ) // read data
|
|
||||||
}
|
|
||||||
|
|
||||||
// node.in(0) is from coalescer TL master node; 1~N are from cores
|
// node.in(0) is from coalescer TL master node; 1~N are from cores
|
||||||
// assert(node.in.length >= 2)
|
// assert(node.in.length >= 2)
|
||||||
val sourceWidth = outer.node.in(1)._1.params.sourceBits
|
val sourceWidth = outer.node.in(1)._1.params.sourceBits
|
||||||
@@ -61,7 +62,7 @@ class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModule
|
|||||||
new ShiftQueue(reqQueueEntryT, 4 /* FIXME hardcoded */ )
|
new ShiftQueue(reqQueueEntryT, 4 /* FIXME hardcoded */ )
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val respQueueEntryT = new RespQueueEntry(sourceWidth)
|
val respQueueEntryT = new RespQueueEntry(sourceWidth, wordSize)
|
||||||
val respQueues = Seq.tabulate(numLanes) { _ =>
|
val respQueues = Seq.tabulate(numLanes) { _ =>
|
||||||
// Module(
|
// Module(
|
||||||
// new ShiftQueue(respQueueEntryT, 8 /* FIXME depth hardcoded */ )
|
// new ShiftQueue(respQueueEntryT, 8 /* FIXME depth hardcoded */ )
|
||||||
@@ -207,14 +208,11 @@ class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModule
|
|||||||
tlCoal.e.valid := false.B
|
tlCoal.e.valid := false.B
|
||||||
|
|
||||||
// Construct new entry for the inflight table
|
// Construct new entry for the inflight table
|
||||||
val inflightTable = Module(
|
// FIXME: don't instantiate inflight table entry type here. It leaks the table's impl
|
||||||
new InflightCoalReqTable(
|
// detail outside to the coalescer
|
||||||
numLanes,
|
val offsetBits = 4 // FIXME hardcoded
|
||||||
sourceWidth,
|
val sizeBits = 2 // FIXME hardcoded
|
||||||
outer.numInflightCoalRequests
|
val newEntry = Wire(new InflightCoalReqTableEntry(numLanes, sourceWidth, offsetBits, sizeBits))
|
||||||
)
|
|
||||||
)
|
|
||||||
val newEntry = Wire(inflightTable.entryT)
|
|
||||||
newEntry.respSourceId := coalSourceId
|
newEntry.respSourceId := coalSourceId
|
||||||
newEntry.lanes.foreach { l =>
|
newEntry.lanes.foreach { l =>
|
||||||
l.valid := false.B
|
l.valid := false.B
|
||||||
@@ -229,16 +227,71 @@ class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModule
|
|||||||
newEntry.lanes(2).valid := true.B
|
newEntry.lanes(2).valid := true.B
|
||||||
dontTouch(newEntry)
|
dontTouch(newEntry)
|
||||||
|
|
||||||
// Populate inflight coalesced request table
|
val coalDataWidth = tlCoal.params.dataBits
|
||||||
inflightTable.io.enq.valid := coalReqValid
|
val uncoalescer = Module(
|
||||||
inflightTable.io.enq.bits := newEntry
|
new UncoalescingUnit(numLanes, sourceWidth, coalDataWidth, outer.numInflightCoalRequests)
|
||||||
|
)
|
||||||
|
|
||||||
|
uncoalescer.io.coalReqValid := coalReqValid
|
||||||
|
uncoalescer.io.newEntry := newEntry
|
||||||
|
uncoalescer.io.coalRespValid := tlCoal.d.valid
|
||||||
|
uncoalescer.io.coalRespSrcId := tlCoal.d.bits.source
|
||||||
|
uncoalescer.io.coalRespData := tlCoal.d.bits.data
|
||||||
|
|
||||||
|
// Queue up uncoalesced responses into each lane's response queue
|
||||||
|
(respQueues zip uncoalescer.io.uncoalResps).foreach { case (q, resp) =>
|
||||||
|
assert(
|
||||||
|
q.io.enq(respQueueCoalPortOffset).ready,
|
||||||
|
s"respQueue: enq port for 0-th coalesced response is blocked"
|
||||||
|
)
|
||||||
|
q.io.enq(respQueueCoalPortOffset).valid := resp.valid
|
||||||
|
q.io.enq(respQueueCoalPortOffset).bits := resp.bits
|
||||||
|
// dontTouch(q.io.enq(respQueueCoalPortOffset))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug
|
||||||
|
dontTouch(coalReqValid)
|
||||||
|
dontTouch(coalReqAddress)
|
||||||
|
val coalRespData = tlCoal.d.bits.data
|
||||||
|
dontTouch(coalRespData)
|
||||||
|
|
||||||
|
dontTouch(tlCoal.a)
|
||||||
|
dontTouch(tlCoal.d)
|
||||||
|
}
|
||||||
|
|
||||||
|
class UncoalescingUnit(
|
||||||
|
val numLanes: Int,
|
||||||
|
val sourceWidth: Int,
|
||||||
|
val coalDataWidth: Int,
|
||||||
|
val numInflightCoalRequests: Int
|
||||||
|
) extends Module {
|
||||||
|
val inflightTable = Module(
|
||||||
|
new InflightCoalReqTable(numLanes, sourceWidth, numInflightCoalRequests)
|
||||||
|
)
|
||||||
|
val wordSize = 4 // FIXME duplicate
|
||||||
|
|
||||||
|
val io = IO(new Bundle {
|
||||||
|
val coalReqValid = Input(Bool())
|
||||||
|
val newEntry = Input(inflightTable.entryT)
|
||||||
|
val coalRespValid = Input(Bool())
|
||||||
|
val coalRespSrcId = Input(UInt(sourceWidth.W))
|
||||||
|
val coalRespData = Input(UInt(coalDataWidth.W))
|
||||||
|
val uncoalResps = Output(Vec(numLanes, ValidIO(new RespQueueEntry(sourceWidth, wordSize))))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Populate inflight table
|
||||||
|
inflightTable.io.enq.valid := io.coalReqValid
|
||||||
|
inflightTable.io.enq.bits := io.newEntry
|
||||||
|
|
||||||
// Look up the table with incoming coalesced responses
|
// Look up the table with incoming coalesced responses
|
||||||
inflightTable.io.lookup.ready := tlCoal.d.valid
|
inflightTable.io.lookup.ready := io.coalRespValid
|
||||||
inflightTable.io.lookupSourceId := tlCoal.d.bits.source
|
inflightTable.io.lookupSourceId := io.coalRespSrcId
|
||||||
val coalDataWidth = tlCoal.params.dataBits
|
|
||||||
val coalRespData = Wire(UInt(coalDataWidth.W))
|
assert(
|
||||||
coalRespData := tlCoal.d.bits.data
|
!((io.coalReqValid === true.B) && (io.coalRespValid === true.B) &&
|
||||||
|
(io.newEntry.respSourceId === io.coalRespSrcId)),
|
||||||
|
"inflight table: enqueueing and looking up the same srcId at the same cycle is not handled"
|
||||||
|
)
|
||||||
|
|
||||||
// Un-coalescing logic
|
// Un-coalescing logic
|
||||||
//
|
//
|
||||||
@@ -256,29 +309,23 @@ class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModule
|
|||||||
chunks(offset) // MUX
|
chunks(offset) // MUX
|
||||||
}
|
}
|
||||||
|
|
||||||
// Un-coalesce responses back to individual lanes and queue them up
|
// Un-coalesce responses back to individual lanes
|
||||||
val found = inflightTable.io.lookup.bits
|
val found = inflightTable.io.lookup.bits
|
||||||
found.lanes.zipWithIndex.foreach { case (l, i) =>
|
found.lanes.zipWithIndex.foreach { case (l, i) =>
|
||||||
// FIXME: only looking at 0th srcId entry
|
// FIXME: only looking at 0th srcId entry
|
||||||
|
|
||||||
val respQueue = respQueues(i)
|
val uncoalResp = io.uncoalResps(i)
|
||||||
assert(
|
uncoalResp.valid := false.B
|
||||||
respQueue.io.enq(respQueueCoalPortOffset).ready,
|
uncoalResp.bits := DontCare
|
||||||
s"respQueue: enq port for 0-th coalesced response is blocked"
|
|
||||||
)
|
|
||||||
dontTouch(respQueue.io.enq(respQueueCoalPortOffset).ready)
|
|
||||||
respQueue.io.enq(respQueueCoalPortOffset).valid := false.B
|
|
||||||
respQueue.io.enq(respQueueCoalPortOffset).bits := DontCare
|
|
||||||
|
|
||||||
when(inflightTable.io.lookup.valid) {
|
when(inflightTable.io.lookup.valid) {
|
||||||
respQueue.io.enq(respQueueCoalPortOffset).valid := l.valid
|
uncoalResp.valid := l.valid
|
||||||
respQueue.io.enq(respQueueCoalPortOffset).bits.source := 0.U
|
uncoalResp.bits.source := 0.U
|
||||||
|
|
||||||
// FIXME: disregard size enum for now
|
// FIXME: disregard size enum for now
|
||||||
val size = wordSize
|
val size = 4
|
||||||
respQueue.io.enq(respQueueCoalPortOffset).bits.data :=
|
uncoalResp.bits.data :=
|
||||||
getCoalescedDataChunk(coalRespData, coalDataWidth, l.reqs(0).offset, size)
|
getCoalescedDataChunk(io.coalRespData, coalDataWidth, l.reqs(0).offset, size)
|
||||||
// chunks(l.reqs(0).offset)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
when(l.valid) {
|
when(l.valid) {
|
||||||
@@ -287,14 +334,6 @@ class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModule
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug
|
|
||||||
dontTouch(coalReqValid)
|
|
||||||
dontTouch(coalReqAddress)
|
|
||||||
dontTouch(coalRespData)
|
|
||||||
|
|
||||||
dontTouch(tlCoal.a)
|
|
||||||
dontTouch(tlCoal.d)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// InflightCoalReqTable is a table structure that records
|
// InflightCoalReqTable is a table structure that records
|
||||||
@@ -302,11 +341,8 @@ class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModule
|
|||||||
// from, what their original TileLink sourceId were, etc. We use this info to
|
// from, what their original TileLink sourceId were, etc. We use this info to
|
||||||
// split the coalesced response back to individual per-lane responses with the
|
// split the coalesced response back to individual per-lane responses with the
|
||||||
// right metadata.
|
// right metadata.
|
||||||
class InflightCoalReqTable(
|
class InflightCoalReqTable(val numLanes: Int, val sourceWidth: Int, val entries: Int)
|
||||||
val numLanes: Int,
|
extends Module {
|
||||||
val sourceWidth: Int,
|
|
||||||
val entries: Int
|
|
||||||
) extends Module {
|
|
||||||
val offsetBits = 4 // FIXME hardcoded
|
val offsetBits = 4 // FIXME hardcoded
|
||||||
val sizeBits = 2 // FIXME hardcoded
|
val sizeBits = 2 // FIXME hardcoded
|
||||||
val entryT =
|
val entryT =
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ import chisel3._
|
|||||||
import chiseltest._
|
import chiseltest._
|
||||||
import org.scalatest.flatspec.AnyFlatSpec
|
import org.scalatest.flatspec.AnyFlatSpec
|
||||||
import freechips.rocketchip.tilelink._
|
import freechips.rocketchip.tilelink._
|
||||||
|
import freechips.rocketchip.diplomacy.LazyModule
|
||||||
import freechips.rocketchip.util.MultiPortQueue
|
import freechips.rocketchip.util.MultiPortQueue
|
||||||
|
import freechips.rocketchip.config._
|
||||||
|
|
||||||
class MultiPortQueueUnitTest extends AnyFlatSpec with ChiselScalatestTester {
|
class MultiPortQueueUnitTest extends AnyFlatSpec with ChiselScalatestTester {
|
||||||
behavior of "MultiPortQueue"
|
behavior of "MultiPortQueue"
|
||||||
|
|
||||||
|
// This is really just to figure out how MultiPortQueue works
|
||||||
it should "serialize at dequeue end" in {
|
it should "serialize at dequeue end" in {
|
||||||
test(new MultiPortQueue(UInt(4.W), 3, 1, 3, 6))
|
test(new MultiPortQueue(UInt(4.W), 3, 1, 3, 6))
|
||||||
.withAnnotations(Seq(WriteVcdAnnotation)) { c =>
|
.withAnnotations(Seq(WriteVcdAnnotation)) { c =>
|
||||||
@@ -28,7 +31,21 @@ class MultiPortQueueUnitTest extends AnyFlatSpec with ChiselScalatestTester {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CoalescingUnitTest extends AnyFlatSpec with ChiselScalatestTester {
|
class CoalescingUnitChiselTest extends AnyFlatSpec with ChiselScalatestTester {
|
||||||
|
behavior of "coalescing unit"
|
||||||
|
val numLanes = 4
|
||||||
|
|
||||||
|
it should "work" in {
|
||||||
|
implicit val p = Parameters((site, here, up) => { i => i })
|
||||||
|
test(new CoalescingUnitTest(timeout = 50)) { c =>
|
||||||
|
for (_ <- 0 until 100) {
|
||||||
|
c.clock.step()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CoalInflightTableUnitTest extends AnyFlatSpec with ChiselScalatestTester {
|
||||||
behavior of "inflight coalesced request table"
|
behavior of "inflight coalesced request table"
|
||||||
val numLanes = 4
|
val numLanes = 4
|
||||||
val sourceWidth = 2
|
val sourceWidth = 2
|
||||||
|
|||||||
Reference in New Issue
Block a user