From 000cbda886fbd2ce9dd949ed2e4024dd03a248aa Mon Sep 17 00:00:00 2001 From: Eliot Yoon Date: Fri, 18 Sep 2026 04:55:30 +0000 Subject: [PATCH] sim: fix TCU tile-buffer response matching past 65536 requests The tile buffer keyed its in-flight map with the unmasked tag counter while the response carried the tag masked to kSubTagMask (0xFFFF): uint32_t sub_tag = buf.next_tag_++; // unmasked, unbounded uint32_t tag = pack_tag(s, sub_tag); // masked on the wire buf.inflight_[sub_tag] = addr; // key stored unmasked and on the way back: uint32_t sub_tag = unpack_sub_tag(r.tag); // masked auto it = buf.inflight_.find(sub_tag); // masked lookup if (it != buf.inflight_.end()) { ... } // no-ops on a miss The first 65536 requests on a source work because the two values agree. Request 65536 is stored under key 65536 but its response comes back carrying 0, so the lookup misses and the branch silently falls through: resident_ never receives the line and the inflight_ entry is never erased. The TCU waits forever for data that cannot arrive. Mask at generation so the key and the wire tag are the same value. rtlsim is unaffected, its tags being properly sized, so this also showed up as a simx/rtlsim divergence. The trigger is a count of tile-buffer requests, not a run length, so it can be reached by sgemm_tcu_wg at input 384^3. Crosses 65536 requests on the shared B tile buffer and hangs, while 256^3 stays under it and completes 57M cycles. Cycle counts below the threshold are unchanged, so the fix is a no-op for any workload that was previously unaffected. --- sim/simx/tcu/tcu_tbuf.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sim/simx/tcu/tcu_tbuf.cpp b/sim/simx/tcu/tcu_tbuf.cpp index f362d74d1e..6465e70f4a 100644 --- a/sim/simx/tcu/tcu_tbuf.cpp +++ b/sim/simx/tcu/tcu_tbuf.cpp @@ -145,7 +145,10 @@ class TcuTbuf::Impl { auto& buf = bufs_.at(s); if (buf.pending_q_.empty()) continue; uint64_t addr = buf.pending_q_.front(); - uint32_t sub_tag = buf.next_tag_++; + // Mask at generation: pack_tag() masks to kSubTagMask on the wire, so an + // unmasked key here stops matching the returned tag once next_tag_ passes + // 0xFFFF -- the response is then dropped and the inflight entry never clears. + uint32_t sub_tag = (buf.next_tag_++) & kSubTagMask; uint32_t tag = pack_tag(s, sub_tag); MemReq m(MemOp::LD, addr, /*data*/nullptr, /*byteen*/0, tag, /*hart_id*/0, /*uuid*/0); m.flags.local = 1; // TCU TBUF reads from LMEM