diff --git a/src/main/scala/tilelink/Coalescing.scala b/src/main/scala/tilelink/Coalescing.scala index 7a91939..ad8e50f 100644 --- a/src/main/scala/tilelink/Coalescing.scala +++ b/src/main/scala/tilelink/Coalescing.scala @@ -218,13 +218,13 @@ class CoalShiftQueue[T <: Data]( } // Software model: coalescer.py -class MonoCoalescer[T <: Data](coalSize: Int, coalWindow: Seq[CoalShiftQueue[T]], +class MonoCoalescer[T <: ReqQueueEntry](coalSize: Int, coalWindow: Seq[CoalShiftQueue[T]], config: CoalescerConfig) extends Module { val io = IO(new Bundle { - val leader_idx = Output(UInt(log2Ceil(config.NUM_LANES).W)) - val base_addr = Output(UInt(config.ADDR_WIDTH.W)) - val match_oh = Output(Vec(config.NUM_LANES, UInt(config.DEPTH.W))) - val coverage_hits = Output(UInt((1 << config.MAX_SIZE).W)) + val leaderIdx = Output(UInt(log2Ceil(config.NUM_LANES).W)) + val baseAddr = Output(UInt(config.ADDR_WIDTH.W)) + val matchOH = Output(Vec(config.NUM_LANES, UInt(config.DEPTH.W))) + val coverageHits = Output(UInt((1 << config.MAX_SIZE).W)) }) io := DontCare @@ -233,19 +233,53 @@ class MonoCoalescer[T <: Data](coalSize: Int, coalWindow: Seq[CoalShiftQueue[T]] val mask = ((1 << config.ADDR_WIDTH - 1) - (1 << size - 1)).U val window = coalWindow - def can_match(req0: Valid[ReqQueueEntry], req1: Valid[ReqQueueEntry]): Bool = { - (req0.bits.op === req1.bits.op) && - (req0.valid && req1.valid) && - ((req0.bits.address & this.mask) === (req1.bits.address & this.mask)) + def canMatch(req0: ReqQueueEntry, req0v: Bool, req1: ReqQueueEntry, req1v: Bool): Bool = { + (req0.op === req1.op) && + (req0v && req1v) && + ((req0.address & this.mask) === (req1.address & this.mask)) } // combinational logic to drive output from window contents + val leaders = window.map(_.io.elts.head) + val leadersValid = window.map(_.io.mask.asBools.head) - val leaders = coalWindow.map(_.io.elts.head) + // TODO: match leader to only lanes >= leader idx + val matches = leaders.zip(leadersValid).map { case (leader, leaderValid) => + window.map {followerLane => + followerLane.io.elts.zip(followerLane.io.mask.asBools).map { case (follower, followerValid) => + this.canMatch(follower, followerValid, leader, leaderValid) + } + } + } + + val matchCounts = matches.map(leader => leader.map(PopCount(_)).reduce(_ + _)) + val canCoalesce = matchCounts.map(_ > 1.U) + + // TODO: maybe use round robin arbiter instead of argmax to pick leader + val chosenLeaderIdx = matchCounts.zipWithIndex.map { + case (a, b) => (a, b.U) + }.reduce { case ((a, i), (b, j)) => + Mux(a > b, (a, i), (b, j)) + }._2 + + // completely unreadable + val chosenLeader = VecInit(window.map(_.io.elts))(chosenLeaderIdx) + val chosenMatches = VecInit(matches.map(leader => VecInit(leader.map(VecInit(_).asUInt))))(chosenLeaderIdx) + + def getOffsetSlice(addr: UInt) = addr(size - 1, config.WORD_WIDTH) + val offsets = window.map(_.io.elts).flatMap(_.map(req => getOffsetSlice(req.address))) + val hits = Seq.tabulate(1 << (size - config.WORD_WIDTH)) { target => + offsets.map(_ === target.U).reduce(_ || _) + } + + io.leaderIdx := chosenLeaderIdx + io.baseAddr := chosenLeader + io.matchOH := chosenMatches + io.coverageHits := PopCount(hits) } // Software model: coalescer.py -class MultiCoalescer[T <: Data] +class MultiCoalescer[T <: ReqQueueEntry] (sizes: Seq[Int], window: Seq[CoalShiftQueue[T]], coalReqT: ReqQueueEntry, config: CoalescerConfig) extends Module {