diff --git a/src/main/scala/tilelink/Coalescing.scala b/src/main/scala/tilelink/Coalescing.scala index 31b650f..24c1c92 100644 --- a/src/main/scala/tilelink/Coalescing.scala +++ b/src/main/scala/tilelink/Coalescing.scala @@ -29,13 +29,18 @@ class CoalescingUnit(numLanes: Int = 1)(implicit p: Parameters) sourceId = IdRange(0, numInflightCoalRequests) ) ) - protected val coalescerNode = TLClientNode( + val coalescerNode = TLClientNode( Seq(TLMasterPortParameters.v1(coalParam)) ) // Connect master node as the first inward edge of the IdentityNode node :=* coalescerNode + lazy val module = new CoalescingUnitImp(this, numLanes) +} + +class CoalescingUnitImp(outer: CoalescingUnit, numLanes: Int) + extends LazyModuleImp(outer) { class ReqQueueEntry(val sourceWidth: Int, val addressWidth: Int) extends Bundle { val source = UInt(sourceWidth.W) @@ -47,208 +52,209 @@ class CoalescingUnit(numLanes: Int = 1)(implicit p: Parameters) val data = UInt(64.W /* FIXME hardcoded */ ) // read data } - lazy val module = new Impl - class Impl extends LazyModuleImp(this) { - // node.in(0) is from coalescer TL master node; 1~N are from cores - // assert(node.in.length >= 2) - val sourceWidth = node.in(1)._1.params.sourceBits - val addressWidth = node.in(1)._1.params.addressBits - val reqQueueEntryT = new ReqQueueEntry(sourceWidth, addressWidth) - val reqQueues = Seq.tabulate(numLanes) { _ => - Module( - new ShiftQueue(reqQueueEntryT, 4 /* FIXME hardcoded */ ) - ) - } - val respQueueEntryT = new RespQueueEntry(sourceWidth) - val respQueues = Seq.tabulate(numLanes) { _ => - Module( - new ShiftQueue(respQueueEntryT, 8 /* FIXME hardcoded */ ) - ) - } - - // Per-lane request and response queues - // - // Override IdentityNode implementation so that we wire node output to the - // queue output, instead of directly passing through node input. - // See IdentityNode definition in `diplomacy/Nodes.scala`. - (node.in zip node.out).zipWithIndex.foreach { - case (((_, edgeIn), _), 0) => - // No need to do anything on the edge from coalescerNode - assert( - edgeIn.master.masters(0).name == "CoalescerNode", - "First edge is not connected to the coalescer master node" - ) - case (((tlIn, edgeIn), (tlOut, edgeOut)), i) => - // Request queue - // - val reqQueue = reqQueues(i - 1) - val req = Wire(reqQueueEntryT) - req.source := tlIn.a.bits.source - req.address := tlIn.a.bits.address - req.data := tlIn.a.bits.data - - println(s"============ req.source width=${req.source.widthOption.get}") - - reqQueue.io.enq.valid := tlIn.a.valid - reqQueue.io.enq.bits := req - // TODO: deq.ready should respect downstream ready - reqQueue.io.deq.ready := true.B - - tlOut.a.valid := reqQueue.io.deq.valid - val reqHead = reqQueue.io.deq.bits - // FIXME: generate Get or Put according to read/write - val (reqLegal, reqBits) = edgeOut.Get( - fromSource = reqHead.source, - // `toAddress` should be aligned to 2**lgSize - toAddress = reqHead.address, - lgSize = 0.U - ) - assert(reqLegal, "unhandled illegal TL req gen") - tlOut.a.bits := reqBits - - // Response queue - // - // This queue will serialize non-coalesced responses along with - // coalesced responses and serve them back to the core side. - val respQueue = respQueues(i - 1) - val resp = Wire(respQueueEntryT) - resp.source := tlOut.d.bits.source - resp.data := tlOut.d.bits.data - - respQueue.io.enq.valid := tlOut.d.valid - respQueue.io.enq.bits := resp - // TODO: deq.ready should respect upstream ready - respQueue.io.deq.ready := true.B - - tlIn.d.valid := respQueue.io.deq.valid - val respHead = respQueue.io.deq.bits - val respBits = edgeIn.AccessAck( - toSource = respHead.source, - lgSize = 0.U, - data = respHead.data - ) - tlIn.d.bits := respBits - - // tlIn.d <> tlOut.d - - // Debug only - val inflightCounter = RegInit(UInt(32.W), 0.U) - when(tlOut.a.valid) { - // don't inc/dec on simultaneous req/resp - when(!tlOut.d.valid) { - inflightCounter := inflightCounter + 1.U - } - }.elsewhen(tlOut.d.valid) { - inflightCounter := inflightCounter - 1.U - } - - dontTouch(inflightCounter) - dontTouch(tlIn.a) - dontTouch(tlIn.d) - dontTouch(tlOut.a) - dontTouch(tlOut.d) - } - - // Generate coalesced requests - // FIXME: currently generating bogus coalesced requests - val coalSourceId = RegInit(0.U(2.W /* FIXME hardcoded */ )) - coalSourceId := coalSourceId + 1.U - - val (tlCoal, edgeCoal) = coalescerNode.out(0) - val coalReqAddress = Wire(UInt(tlCoal.params.addressBits.W)) - // TODO: bogus address - coalReqAddress := (0xabcd.U + coalSourceId) << 4 - val coalReqValid = Wire(Bool()) - // FIXME: copy lane 1's valid signal. This is completely bogus - coalReqValid := node.in(1)._1.a.valid - - val (legal, bits) = edgeCoal.Get( - fromSource = coalSourceId, - // `toAddress` should be aligned to 2**lgSize - toAddress = coalReqAddress, - // 64 bits = 8 bytes = 2**(3) bytes - lgSize = 3.U + // node.in(0) is from coalescer TL master node; 1~N are from cores + // assert(node.in.length >= 2) + val sourceWidth = outer.node.in(1)._1.params.sourceBits + val addressWidth = outer.node.in(1)._1.params.addressBits + val reqQueueEntryT = new ReqQueueEntry(sourceWidth, addressWidth) + val reqQueues = Seq.tabulate(numLanes) { _ => + Module( + new ShiftQueue(reqQueueEntryT, 4 /* FIXME hardcoded */ ) ) - assert(legal, "unhandled illegal TL req gen") - tlCoal.a.valid := coalReqValid - tlCoal.a.bits := bits - tlCoal.b.ready := true.B - tlCoal.c.valid := false.B - tlCoal.d.ready := true.B - tlCoal.e.valid := false.B - - // Construct new entry for the inflight table - val inflightTable = Module( - new InflightCoalReqTable(numLanes, sourceWidth, numInflightCoalRequests) - ) - val newEntry = Wire(inflightTable.entryT) - newEntry.respSourceId := coalSourceId - newEntry.lanes.foreach { l => - l.valid := false.B - l.reqs.foreach { r => - // TODO: this part needs the actual coalescing logic to work - r.valid := true.B - r.offset := 1.U - r.size := 2.U - } - } - newEntry.lanes(0).valid := true.B - newEntry.lanes(2).valid := true.B - dontTouch(newEntry) - - // Populate inflight coalesced request table - inflightTable.io.enq.valid := coalReqValid - inflightTable.io.enq.bits := newEntry - - // Look up the table with incoming coalesced responses - inflightTable.io.lookup.ready := tlCoal.d.valid - inflightTable.io.lookupSourceId := tlCoal.d.bits.source - val coalRespData = Wire(UInt(tlCoal.params.dataBits.W)) - coalRespData := tlCoal.d.bits.data - - when(inflightTable.io.lookup.valid) { - val found = inflightTable.io.lookup.bits - found.lanes.zipWithIndex.foreach { case (l, i) => - val respQueue = respQueues(i) - respQueue.io.enq.valid := l.valid - respQueue.io.enq.bits.source := 0.U // FIXME: only looking at 0th entry - // FIXME: disregard size enum for now - val sizeMask = (1.U << 4) - 1.U - val dataWidth = tlCoal.params.dataBits - // FIXME: handle multi-head input to the queue - respQueue.io.enq.bits.data := (coalRespData >> (dataWidth - 4)) & sizeMask - - when(l.valid) { - when(l.reqs(0).valid) { - printf(s"lane ${i} req 0 is valid!\n") - } - } - } - } - - (node.in zip node.out)(0) match { - case ((tlIn, edgeIn), (tlOut, _)) => - assert( - edgeIn.master.masters.length == 1 && - edgeIn.master.masters(0).name == "CoalescerNode", - "First edge is not connected to the coalescer master node" - ) - - // TODO: do we need to do anything here? - tlOut.a <> tlIn.a - tlIn.d <> tlOut.d - dontTouch(tlIn.d) - dontTouch(tlOut.d) - } - - // Debug - dontTouch(coalReqValid) - dontTouch(coalReqAddress) - dontTouch(coalRespData) - - dontTouch(tlCoal.a) - dontTouch(tlCoal.d) } + val respQueueEntryT = new RespQueueEntry(sourceWidth) + val respQueues = Seq.tabulate(numLanes) { _ => + Module( + new ShiftQueue(respQueueEntryT, 8 /* FIXME hardcoded */ ) + ) + } + + // Per-lane request and response queues + // + // Override IdentityNode implementation so that we wire node output to the + // queue output, instead of directly passing through node input. + // See IdentityNode definition in `diplomacy/Nodes.scala`. + (outer.node.in zip outer.node.out).zipWithIndex.foreach { + case (((_, edgeIn), _), 0) => + // No need to do anything on the edge from coalescerNode + assert( + edgeIn.master.masters(0).name == "CoalescerNode", + "First edge is not connected to the coalescer master node" + ) + case (((tlIn, edgeIn), (tlOut, edgeOut)), i) => + // Request queue + // + val reqQueue = reqQueues(i - 1) + val req = Wire(reqQueueEntryT) + req.source := tlIn.a.bits.source + req.address := tlIn.a.bits.address + req.data := tlIn.a.bits.data + + println(s"============ req.source width=${req.source.widthOption.get}") + + reqQueue.io.enq.valid := tlIn.a.valid + reqQueue.io.enq.bits := req + // TODO: deq.ready should respect downstream ready + reqQueue.io.deq.ready := true.B + + tlOut.a.valid := reqQueue.io.deq.valid + val reqHead = reqQueue.io.deq.bits + // FIXME: generate Get or Put according to read/write + val (reqLegal, reqBits) = edgeOut.Get( + fromSource = reqHead.source, + // `toAddress` should be aligned to 2**lgSize + toAddress = reqHead.address, + lgSize = 0.U + ) + assert(reqLegal, "unhandled illegal TL req gen") + tlOut.a.bits := reqBits + + // Response queue + // + // This queue will serialize non-coalesced responses along with + // coalesced responses and serve them back to the core side. + val respQueue = respQueues(i - 1) + val resp = Wire(respQueueEntryT) + resp.source := tlOut.d.bits.source + resp.data := tlOut.d.bits.data + + respQueue.io.enq.valid := tlOut.d.valid + respQueue.io.enq.bits := resp + // TODO: deq.ready should respect upstream ready + respQueue.io.deq.ready := true.B + + tlIn.d.valid := respQueue.io.deq.valid + val respHead = respQueue.io.deq.bits + val respBits = edgeIn.AccessAck( + toSource = respHead.source, + lgSize = 0.U, + data = respHead.data + ) + tlIn.d.bits := respBits + + // tlIn.d <> tlOut.d + + // Debug only + val inflightCounter = RegInit(UInt(32.W), 0.U) + when(tlOut.a.valid) { + // don't inc/dec on simultaneous req/resp + when(!tlOut.d.valid) { + inflightCounter := inflightCounter + 1.U + } + }.elsewhen(tlOut.d.valid) { + inflightCounter := inflightCounter - 1.U + } + + dontTouch(inflightCounter) + dontTouch(tlIn.a) + dontTouch(tlIn.d) + dontTouch(tlOut.a) + dontTouch(tlOut.d) + } + + // Generate coalesced requests + // FIXME: currently generating bogus coalesced requests + val coalSourceId = RegInit(0.U(2.W /* FIXME hardcoded */ )) + coalSourceId := coalSourceId + 1.U + + val (tlCoal, edgeCoal) = outer.coalescerNode.out(0) + val coalReqAddress = Wire(UInt(tlCoal.params.addressBits.W)) + // TODO: bogus address + coalReqAddress := (0xabcd.U + coalSourceId) << 4 + val coalReqValid = Wire(Bool()) + // FIXME: copy lane 1's valid signal. This is completely bogus + coalReqValid := outer.node.in(1)._1.a.valid + + val (legal, bits) = edgeCoal.Get( + fromSource = coalSourceId, + // `toAddress` should be aligned to 2**lgSize + toAddress = coalReqAddress, + // 64 bits = 8 bytes = 2**(3) bytes + lgSize = 3.U + ) + assert(legal, "unhandled illegal TL req gen") + tlCoal.a.valid := coalReqValid + tlCoal.a.bits := bits + tlCoal.b.ready := true.B + tlCoal.c.valid := false.B + tlCoal.d.ready := true.B + tlCoal.e.valid := false.B + + // Construct new entry for the inflight table + val inflightTable = Module( + new InflightCoalReqTable( + numLanes, + sourceWidth, + outer.numInflightCoalRequests + ) + ) + val newEntry = Wire(inflightTable.entryT) + newEntry.respSourceId := coalSourceId + newEntry.lanes.foreach { l => + l.valid := false.B + l.reqs.foreach { r => + // TODO: this part needs the actual coalescing logic to work + r.valid := true.B + r.offset := 1.U + r.size := 2.U + } + } + newEntry.lanes(0).valid := true.B + newEntry.lanes(2).valid := true.B + dontTouch(newEntry) + + // Populate inflight coalesced request table + inflightTable.io.enq.valid := coalReqValid + inflightTable.io.enq.bits := newEntry + + // Look up the table with incoming coalesced responses + inflightTable.io.lookup.ready := tlCoal.d.valid + inflightTable.io.lookupSourceId := tlCoal.d.bits.source + val coalRespData = Wire(UInt(tlCoal.params.dataBits.W)) + coalRespData := tlCoal.d.bits.data + + when(inflightTable.io.lookup.valid) { + val found = inflightTable.io.lookup.bits + found.lanes.zipWithIndex.foreach { case (l, i) => + val respQueue = respQueues(i) + respQueue.io.enq.valid := l.valid + respQueue.io.enq.bits.source := 0.U // FIXME: only looking at 0th entry + // FIXME: disregard size enum for now + val sizeMask = (1.U << 4) - 1.U + val dataWidth = tlCoal.params.dataBits + // FIXME: handle multi-head input to the queue + respQueue.io.enq.bits.data := (coalRespData >> (dataWidth - 4)) & sizeMask + + when(l.valid) { + when(l.reqs(0).valid) { + printf(s"lane ${i} req 0 is valid!\n") + } + } + } + } + + (outer.node.in zip outer.node.out)(0) match { + case ((tlIn, edgeIn), (tlOut, _)) => + assert( + edgeIn.master.masters.length == 1 && + edgeIn.master.masters(0).name == "CoalescerNode", + "First edge is not connected to the coalescer master node" + ) + + // TODO: do we need to do anything here? + tlOut.a <> tlIn.a + tlIn.d <> tlOut.d + dontTouch(tlIn.d) + dontTouch(tlOut.d) + } + + // Debug + dontTouch(coalReqValid) + dontTouch(coalReqAddress) + dontTouch(coalRespData) + + dontTouch(tlCoal.a) + dontTouch(tlCoal.d) } // InflightCoalReqTable is a reservation station-like structure that records