Basic unit test for the uncoalescer
This commit is contained in:
@@ -44,9 +44,9 @@ class ReqQueueEntry(val sourceWidth: Int, val addressWidth: Int) extends Bundle
|
|||||||
val data = UInt(64.W /* FIXME hardcoded */ ) // write data
|
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 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) {
|
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 */ )
|
new ShiftQueue(reqQueueEntryT, 4 /* FIXME hardcoded */ )
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val respQueueEntryT = new RespQueueEntry(sourceWidth, wordSize)
|
val respQueueEntryT = new RespQueueEntry(sourceWidth, wordSize * 8)
|
||||||
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 */ )
|
||||||
@@ -140,8 +140,8 @@ class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModule
|
|||||||
resp.source := tlOut.d.bits.source
|
resp.source := tlOut.d.bits.source
|
||||||
resp.data := tlOut.d.bits.data
|
resp.data := tlOut.d.bits.data
|
||||||
|
|
||||||
// Originally non-coalesced responses. Coalesced (but split) responses
|
// Queue up responses that didn't get coalesced originally ("noncoalesced" responses).
|
||||||
// will also be enqueued into the same queue.
|
// Coalesced (but uncoalesced back) responses will also be enqueued into the same queue.
|
||||||
assert(
|
assert(
|
||||||
respQueue.io.enq(respQueueNoncoalPort).ready,
|
respQueue.io.enq(respQueueNoncoalPort).ready,
|
||||||
"respQueue: enq port for noncoalesced response is blocked"
|
"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.bits := respBits
|
||||||
|
|
||||||
// tlIn.d <> tlOut.d
|
|
||||||
|
|
||||||
// Debug only
|
// Debug only
|
||||||
val inflightCounter = RegInit(UInt(32.W), 0.U)
|
val inflightCounter = RegInit(UInt(32.W), 0.U)
|
||||||
when(tlOut.a.valid) {
|
when(tlOut.a.valid) {
|
||||||
@@ -213,7 +211,7 @@ class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModule
|
|||||||
val offsetBits = 4 // FIXME hardcoded
|
val offsetBits = 4 // FIXME hardcoded
|
||||||
val sizeBits = 2 // FIXME hardcoded
|
val sizeBits = 2 // FIXME hardcoded
|
||||||
val newEntry = Wire(new InflightCoalReqTableEntry(numLanes, sourceWidth, offsetBits, sizeBits))
|
val newEntry = Wire(new InflightCoalReqTableEntry(numLanes, sourceWidth, offsetBits, sizeBits))
|
||||||
newEntry.respSourceId := coalSourceId
|
newEntry.source := coalSourceId
|
||||||
newEntry.lanes.foreach { l =>
|
newEntry.lanes.foreach { l =>
|
||||||
l.valid := false.B
|
l.valid := false.B
|
||||||
l.reqs.foreach { r =>
|
l.reqs.foreach { r =>
|
||||||
@@ -227,6 +225,7 @@ class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) extends LazyModule
|
|||||||
newEntry.lanes(2).valid := true.B
|
newEntry.lanes(2).valid := true.B
|
||||||
dontTouch(newEntry)
|
dontTouch(newEntry)
|
||||||
|
|
||||||
|
// Uncoalescer module sncoalesces responses back to each lane
|
||||||
val coalDataWidth = tlCoal.params.dataBits
|
val coalDataWidth = tlCoal.params.dataBits
|
||||||
val uncoalescer = Module(
|
val uncoalescer = Module(
|
||||||
new UncoalescingUnit(numLanes, sourceWidth, coalDataWidth, outer.numInflightCoalRequests)
|
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).valid := resp.valid
|
||||||
q.io.enq(respQueueCoalPortOffset).bits := resp.bits
|
q.io.enq(respQueueCoalPortOffset).bits := resp.bits
|
||||||
// dontTouch(q.io.enq(respQueueCoalPortOffset))
|
// dontTouch(q.io.enq(respQueueCoalPortOffset))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug
|
// Debug
|
||||||
@@ -276,7 +275,7 @@ class UncoalescingUnit(
|
|||||||
val coalRespValid = Input(Bool())
|
val coalRespValid = Input(Bool())
|
||||||
val coalRespSrcId = Input(UInt(sourceWidth.W))
|
val coalRespSrcId = Input(UInt(sourceWidth.W))
|
||||||
val coalRespData = Input(UInt(coalDataWidth.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
|
// Populate inflight table
|
||||||
@@ -289,22 +288,23 @@ class UncoalescingUnit(
|
|||||||
|
|
||||||
assert(
|
assert(
|
||||||
!((io.coalReqValid === true.B) && (io.coalRespValid === true.B) &&
|
!((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"
|
"inflight table: enqueueing and looking up the same srcId at the same cycle is not handled"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Un-coalescing logic
|
// Un-coalescing logic
|
||||||
//
|
//
|
||||||
// FIXME: `size` should be UInt, not Int
|
// FIXME: `size` should be UInt, not Int
|
||||||
def getCoalescedDataChunk(data: UInt, dataWidth: Int, offset: UInt, size: Int): UInt = {
|
def getCoalescedDataChunk(data: UInt, dataWidth: Int, offset: UInt, byteSize: Int): UInt = {
|
||||||
val sizeMask = (1.U << size) - 1.U
|
val bitSize = byteSize * 8
|
||||||
assert(dataWidth % size == 0, "coalesced data width not evenly divisible by size")
|
val sizeMask = (1.U << bitSize) - 1.U
|
||||||
val numChunks = dataWidth / size
|
assert(dataWidth % bitSize == 0, "coalesced data width not evenly divisible by size")
|
||||||
val chunks = Wire(Vec(numChunks, UInt(size.W)))
|
val numChunks = dataWidth / bitSize
|
||||||
|
val chunks = Wire(Vec(numChunks, UInt(bitSize.W)))
|
||||||
val offsets = (0 until numChunks)
|
val offsets = (0 until numChunks)
|
||||||
(chunks zip offsets).foreach { case (c, o) =>
|
(chunks zip offsets).foreach { case (c, o) =>
|
||||||
// Take [(off-1)*size:off*size] starting from MSB
|
// 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
|
chunks(offset) // MUX
|
||||||
}
|
}
|
||||||
@@ -323,9 +323,9 @@ class UncoalescingUnit(
|
|||||||
uncoalResp.bits.source := 0.U
|
uncoalResp.bits.source := 0.U
|
||||||
|
|
||||||
// FIXME: disregard size enum for now
|
// FIXME: disregard size enum for now
|
||||||
val size = 4
|
val byteSize = 4
|
||||||
uncoalResp.bits.data :=
|
uncoalResp.bits.data :=
|
||||||
getCoalescedDataChunk(io.coalRespData, coalDataWidth, l.reqs(0).offset, size)
|
getCoalescedDataChunk(io.coalRespData, coalDataWidth, l.reqs(0).offset, byteSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
when(l.valid) {
|
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
|
val enqFire = io.enq.ready && io.enq.valid
|
||||||
when(enqFire) {
|
when(enqFire) {
|
||||||
// TODO: handle enqueueing and looking up the same entry in the same cycle?
|
// 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(
|
assert(
|
||||||
!entryToWrite.valid,
|
!entryToWrite.valid,
|
||||||
"tried to enqueue to an already occupied entry"
|
"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
|
// sourceId of the coalesced response that just came back. This will be the
|
||||||
// key that queries the table.
|
// key that queries the table.
|
||||||
val respSourceId = UInt(sourceWidth.W)
|
val source = UInt(sourceWidth.W)
|
||||||
val lanes = Vec(numLanes, new PerLane)
|
val lanes = Vec(numLanes, new PerLane)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ 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"
|
||||||
@@ -31,17 +29,55 @@ class MultiPortQueueUnitTest extends AnyFlatSpec with ChiselScalatestTester {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class CoalescingUnitChiselTest extends AnyFlatSpec with ChiselScalatestTester {
|
class UncoalescingUnitTest extends AnyFlatSpec with ChiselScalatestTester {
|
||||||
behavior of "coalescing unit"
|
behavior of "uncoalescer"
|
||||||
val numLanes = 4
|
val numLanes = 4
|
||||||
|
val sourceWidth = 2
|
||||||
|
// 16B coalescing size
|
||||||
|
val coalDataWidth = 128
|
||||||
|
val numInflightCoalRequests = 4
|
||||||
|
|
||||||
it should "work" in {
|
it should "work" in {
|
||||||
implicit val p = Parameters((site, here, up) => { i => i })
|
test(new UncoalescingUnit(numLanes, sourceWidth, coalDataWidth, numInflightCoalRequests))
|
||||||
test(new CoalescingUnitTest(timeout = 50)) { c =>
|
// vcs helps with simulation time, but sometimes errors with
|
||||||
for (_ <- 0 until 100) {
|
// "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.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)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user