Basic unit test for the uncoalescer

This commit is contained in:
Hansung Kim
2023-03-29 00:49:04 -07:00
parent 9bc8f0074b
commit 3b335bda18
2 changed files with 65 additions and 29 deletions

View File

@@ -44,9 +44,9 @@ class ReqQueueEntry(val sourceWidth: Int, val addressWidth: Int) extends Bundle
val data = UInt(64.W /* FIXME hardcoded */ ) // write data
}
class RespQueueEntry(val sourceWidth: Int, val dataWidth: Int) extends Bundle {
class RespQueueEntry(val sourceWidth: Int, val dataWidthInBits: Int) extends Bundle {
val source = UInt(sourceWidth.W)
val data = UInt(dataWidth.W) // read data
val data = UInt(dataWidthInBits.W) // read data
}
class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModuleImp(outer) {
@@ -62,7 +62,7 @@ class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModule
new ShiftQueue(reqQueueEntryT, 4 /* FIXME hardcoded */ )
)
}
val respQueueEntryT = new RespQueueEntry(sourceWidth, wordSize)
val respQueueEntryT = new RespQueueEntry(sourceWidth, wordSize * 8)
val respQueues = Seq.tabulate(numLanes) { _ =>
// Module(
// new ShiftQueue(respQueueEntryT, 8 /* FIXME depth hardcoded */ )
@@ -140,8 +140,8 @@ class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModule
resp.source := tlOut.d.bits.source
resp.data := tlOut.d.bits.data
// Originally non-coalesced responses. Coalesced (but split) responses
// will also be enqueued into the same queue.
// Queue up responses that didn't get coalesced originally ("noncoalesced" responses).
// Coalesced (but uncoalesced back) responses will also be enqueued into the same queue.
assert(
respQueue.io.enq(respQueueNoncoalPort).ready,
"respQueue: enq port for noncoalesced response is blocked"
@@ -160,8 +160,6 @@ class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModule
)
tlIn.d.bits := respBits
// tlIn.d <> tlOut.d
// Debug only
val inflightCounter = RegInit(UInt(32.W), 0.U)
when(tlOut.a.valid) {
@@ -213,7 +211,7 @@ class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModule
val offsetBits = 4 // FIXME hardcoded
val sizeBits = 2 // FIXME hardcoded
val newEntry = Wire(new InflightCoalReqTableEntry(numLanes, sourceWidth, offsetBits, sizeBits))
newEntry.respSourceId := coalSourceId
newEntry.source := coalSourceId
newEntry.lanes.foreach { l =>
l.valid := false.B
l.reqs.foreach { r =>
@@ -227,6 +225,7 @@ class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModule
newEntry.lanes(2).valid := true.B
dontTouch(newEntry)
// Uncoalescer module sncoalesces responses back to each lane
val coalDataWidth = tlCoal.params.dataBits
val uncoalescer = Module(
new UncoalescingUnit(numLanes, sourceWidth, coalDataWidth, outer.numInflightCoalRequests)
@@ -246,7 +245,7 @@ class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModule
)
q.io.enq(respQueueCoalPortOffset).valid := resp.valid
q.io.enq(respQueueCoalPortOffset).bits := resp.bits
// dontTouch(q.io.enq(respQueueCoalPortOffset))
// dontTouch(q.io.enq(respQueueCoalPortOffset))
}
// Debug
@@ -276,7 +275,7 @@ class UncoalescingUnit(
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))))
val uncoalResps = Output(Vec(numLanes, ValidIO(new RespQueueEntry(sourceWidth, wordSize * 8))))
})
// Populate inflight table
@@ -289,22 +288,23 @@ class UncoalescingUnit(
assert(
!((io.coalReqValid === true.B) && (io.coalRespValid === true.B) &&
(io.newEntry.respSourceId === io.coalRespSrcId)),
(io.newEntry.source === io.coalRespSrcId)),
"inflight table: enqueueing and looking up the same srcId at the same cycle is not handled"
)
// Un-coalescing logic
//
// FIXME: `size` should be UInt, not Int
def getCoalescedDataChunk(data: UInt, dataWidth: Int, offset: UInt, size: Int): UInt = {
val sizeMask = (1.U << size) - 1.U
assert(dataWidth % size == 0, "coalesced data width not evenly divisible by size")
val numChunks = dataWidth / size
val chunks = Wire(Vec(numChunks, UInt(size.W)))
def getCoalescedDataChunk(data: UInt, dataWidth: Int, offset: UInt, byteSize: Int): UInt = {
val bitSize = byteSize * 8
val sizeMask = (1.U << bitSize) - 1.U
assert(dataWidth % bitSize == 0, "coalesced data width not evenly divisible by size")
val numChunks = dataWidth / bitSize
val chunks = Wire(Vec(numChunks, UInt(bitSize.W)))
val offsets = (0 until numChunks)
(chunks zip offsets).foreach { case (c, o) =>
// Take [(off-1)*size:off*size] starting from MSB
c := (data >> (dataWidth - (o + 1) * size)) & sizeMask
c := (data >> (dataWidth - (o + 1) * bitSize)) & sizeMask
}
chunks(offset) // MUX
}
@@ -323,9 +323,9 @@ class UncoalescingUnit(
uncoalResp.bits.source := 0.U
// FIXME: disregard size enum for now
val size = 4
val byteSize = 4
uncoalResp.bits.data :=
getCoalescedDataChunk(io.coalRespData, coalDataWidth, l.reqs(0).offset, size)
getCoalescedDataChunk(io.coalRespData, coalDataWidth, l.reqs(0).offset, byteSize)
}
when(l.valid) {
@@ -398,7 +398,7 @@ class InflightCoalReqTable(val numLanes: Int, val sourceWidth: Int, val entries:
val enqFire = io.enq.ready && io.enq.valid
when(enqFire) {
// TODO: handle enqueueing and looking up the same entry in the same cycle?
val entryToWrite = table(io.enq.bits.respSourceId)
val entryToWrite = table(io.enq.bits.source)
assert(
!entryToWrite.valid,
"tried to enqueue to an already occupied entry"
@@ -438,7 +438,7 @@ class InflightCoalReqTableEntry(
}
// sourceId of the coalesced response that just came back. This will be the
// key that queries the table.
val respSourceId = UInt(sourceWidth.W)
val source = UInt(sourceWidth.W)
val lanes = Vec(numLanes, new PerLane)
}

View File

@@ -2,9 +2,7 @@ import chisel3._
import chiseltest._
import org.scalatest.flatspec.AnyFlatSpec
import freechips.rocketchip.tilelink._
import freechips.rocketchip.diplomacy.LazyModule
import freechips.rocketchip.util.MultiPortQueue
import freechips.rocketchip.config._
class MultiPortQueueUnitTest extends AnyFlatSpec with ChiselScalatestTester {
behavior of "MultiPortQueue"
@@ -31,17 +29,55 @@ class MultiPortQueueUnitTest extends AnyFlatSpec with ChiselScalatestTester {
}
}
class CoalescingUnitChiselTest extends AnyFlatSpec with ChiselScalatestTester {
behavior of "coalescing unit"
class UncoalescingUnitTest extends AnyFlatSpec with ChiselScalatestTester {
behavior of "uncoalescer"
val numLanes = 4
val sourceWidth = 2
// 16B coalescing size
val coalDataWidth = 128
val numInflightCoalRequests = 4
it should "work" in {
implicit val p = Parameters((site, here, up) => { i => i })
test(new CoalescingUnitTest(timeout = 50)) { c =>
for (_ <- 0 until 100) {
test(new UncoalescingUnit(numLanes, sourceWidth, coalDataWidth, numInflightCoalRequests))
// vcs helps with simulation time, but sometimes errors with
// "mutation occurred during iteration" java error
// .withAnnotations(Seq(VcsBackendAnnotation))
{ c =>
val sourceId = 0.U
c.io.coalReqValid.poke(true.B)
c.io.newEntry.source.poke(sourceId)
c.io.newEntry.lanes.foreach { l => l.valid.poke(false.B) }
c.io.newEntry.lanes(0).valid.poke(true.B)
c.io.newEntry.lanes(0).reqs(0).valid.poke(true.B)
c.io.newEntry.lanes(0).reqs(0).offset.poke(1.U)
c.io.newEntry.lanes(0).reqs(0).size.poke(2.U)
c.io.newEntry.lanes(2).valid.poke(true.B)
c.io.newEntry.lanes(2).reqs(0).valid.poke(true.B)
c.io.newEntry.lanes(2).reqs(0).offset.poke(2.U)
c.io.newEntry.lanes(2).reqs(0).size.poke(1.U)
c.clock.step()
c.io.coalReqValid.poke(false.B)
c.clock.step()
c.io.coalRespValid.poke(true.B)
c.io.coalRespSrcId.poke(sourceId)
val lit = (BigInt(0x0123456789abcdefL) << 64) | BigInt(0x5ca1ab1edeadbeefL)
c.io.coalRespData.poke(lit.U)
// table lookup is combinational at the same cycle
c.io.uncoalResps(0).valid.expect(true.B)
c.io.uncoalResps(1).valid.expect(false.B)
c.io.uncoalResps(2).valid.expect(true.B)
c.io.uncoalResps(3).valid.expect(false.B)
c.io.uncoalResps(0).bits.data.expect(0x89abcdefL.U)
c.io.uncoalResps(0).bits.source.expect(0.U)
c.io.uncoalResps(2).bits.data.expect(0x5ca1ab1eL.U)
c.io.uncoalResps(2).bits.source.expect(0.U)
}
}
}
}