Make uncoalescer a separate module for better testability

This commit is contained in:
Hansung Kim
2023-03-28 17:07:54 -07:00
parent 23d8fa3be1
commit 9bc8f0074b
2 changed files with 109 additions and 56 deletions

View File

@@ -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 =

View File

@@ -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