From 3b335bda185a5b9ba179e1f423674847095b7ef8 Mon Sep 17 00:00:00 2001 From: Hansung Kim Date: Wed, 29 Mar 2023 00:49:04 -0700 Subject: [PATCH] Basic unit test for the uncoalescer --- src/main/scala/tilelink/Coalescing.scala | 42 +++++++++---------- src/test/scala/CoalescingUnitTest.scala | 52 ++++++++++++++++++++---- 2 files changed, 65 insertions(+), 29 deletions(-) diff --git a/src/main/scala/tilelink/Coalescing.scala b/src/main/scala/tilelink/Coalescing.scala index db7d3fd..cc19ebc 100644 --- a/src/main/scala/tilelink/Coalescing.scala +++ b/src/main/scala/tilelink/Coalescing.scala @@ -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) } diff --git a/src/test/scala/CoalescingUnitTest.scala b/src/test/scala/CoalescingUnitTest.scala index ec1ecaa..f4fe253 100644 --- a/src/test/scala/CoalescingUnitTest.scala +++ b/src/test/scala/CoalescingUnitTest.scala @@ -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) } - } } }