SC ↔ MXU 握手
本页中的每个 op 名、symbol、operand 数量、assertion string 和 literal,均从
libtpu-0.0.40-cp314wheel(BuildID md589edbbe81c5b328a958fe628a9f2207d;buildlibtpu_lts_20260413_b_RC00,LLVM SHA8918319853fbdf9e6f6cb69e96848f913a22bc31)里的libtpu.so逐字节读取而来。其他版本会不同。
摘要
本页是 SparseCore(SC)协处理器与 TensorCore(TC)systolic MXU 之间的 跨引擎边界 — 这个握手让 recommender / DLRM 模型能够把 SC 中 gather 出来的 embedding row 送入 dense matmul。SC 擅长的正是 MXU 做不了的事(按 index gather 单条 HBM row、atomic FP-add scatter);MXU 擅长的正是 SC 做不了的事(128×128 weight-stationary dense matmul)。XlaSparseDenseMatmul 是二者的融合:SC gather 每个 sample 的 embedding row,MXU 将它们与 dense MLP weights 相乘。两个引擎异步运行,因此整个边界归结为三件事:数据如何跨越(SC 将 tile 写入 TC 的 VMEM operand pool)、引擎如何同步(SFLAG sync-flag 协议和 TC 侧 tpu.sem_wait)、以及 MXU 如何绑定结果(contracting-depth 契约 — SC tile 作为 MXU 的 moving operand 落入,并在 dense MxuContractingSize 上 reduce,而不是在任何 sparse contracting dimension 上 reduce)。
本页只记录边界。SC 内部 gather datapath(Stream slot、IndirectStream 形式、逐元素地址公式)归 Stream Gather/Scatter 所有;TC MXU op family(vlatch / vmatprep / vmatmul / vmatres)归 MXU Slot 所有;SC on-chip geometry 和 bump allocator 归 SparseCore Architecture 所有。这里把它们连接起来。三个 handoff channel(HBM bulk、VMEM-targeted DMA、SFLAG control plane)以及发出它们的 lowering symbol 是可恢复表面;routing key(DeviceAndCoreIds)的 byte layout 和按 generation 划分的 SFLAG-address bit encoding 是 open item。
重新实现时,契约是:
- 恰好有一条直接 SC→VMEM 数据路由,以及一条直接 VMEM→SC 路由。 在 40 个
mlir::sparse_core::tpu_dma_<src>_to_<dst>_sc_<form>op 中,只有tpu_dma_hbm_to_vmem_sc_general(SC→MXU feed)和tpu_dma_vmem_to_hbm_sc_general(MXU result writeback)跨越 SC↔TC VMEM 边界,另有tpu_dma_vmem_to_vmem_sc_general。三者都仅为general形式(16 个 operand,完整 descriptor)。所有其他 SC↔TC transfer 都经由 HBM。重新实现者必须通过这一个 op 路由 embedding-feed tile。 - 同步是 writer-count SFLAG,由 TC 侧等待。 SC 在 tile 完成时升起 sync flag;TC 的
tpu.sem_wait(SemaphoreWaitOp,2 个 operand)在该 flag 的 writer count 达到阈值时前进。该 flag 位于用于 cross-CORE 协调的全局sflag子空间;cross-sub-engine flag(sflag_scs、sflag_tile)是 SC 内部的,不会直接跨到 TC。 - MXU 将 SC tile 作为 dense moving operand 消费。 matmul 在
Target::MxuContractingSize上 reduce(基础为 128,Ghostlite 类为 256)。MxuSparseContractingSize在每个 generation 上都是 0 — sparse-MXU contracting path 在此 build 中未使用,因此 embedding-feed matmul 运行普通 dense contracting datapath。SC-vs-MXU 的分割发生在 engine 层级,不是 sparse contracting mode。 - pipeline 是 double-buffered。
OffloadFactory::AllocateSflag(builder, /*set*/false, /*count*/2)为每个 buffer 分配一个 flag:SC 填充 buffer A 的同时 MXU drain buffer B,然后二者翻转。这隐藏了 SC gather latency,让它与 MXU compute 重叠。
| HLO 边界标记 | XlaSparseDenseMatmulOp(+ Grad / CSR / optimizer-fused / minibatch / megachip variants) |
| SC→MXU 数据路由 | mlir::sparse_core::tpu_dma_hbm_to_vmem_sc_general(16 operands;仅 general 形式) |
| MXU→SC writeback | mlir::sparse_core::tpu_dma_vmem_to_hbm_sc_general(16 operands) |
| DMA lowering | LowerMemrefToMlo::lowerEnqueueDma(0x135105a0)→ sc_tpu.dma_general_start + sc_tpu.dma_wait |
| kStream vs kDma | xla::tpu::sparse_core::GetTransferKind(0x1351b140;(Target&, src, dst, 4×bool)) |
| TC 侧同步 op | tpu.sem_wait / tpu.sem_signal / tpu.sem_read / tpu.fetch_and_add_sync |
| SFLAG 子空间 | sflag(cross-CORE)· sflag_scs(SCS scope)· sflag_tile(TEC/tile scope) |
| Cross-sub-engine sync | OffloadFactory::SyncScsWithTec(0x133e9260)/ SyncTecWithScs(0x133e8fe0) |
| MXU contracting depth | Target::MxuContractingSize = 128 base / 256 Ghostlite class;MxuSparseContractingSize = 0(未使用) |
| Double-buffer | OffloadFactory::AllocateSflag(b, false, 2) — 每个 buffer 一个 flag |
三个 Handoff Channel
目的
SC 和 TC 共享三种物理介质:HBM(全局 table store)、TC 的 VMEM(MXU operand feed)和 SFLAG register file(control plane)。编译器为每次 transfer 选择一个 channel;这个选择决定了该握手是慢速全局 round-trip,还是快速直接 operand feed。
Channel
Channel 1 — HBM bulk(慢速,全局):
SC stream engine --STREAM_OPCODE_GATHER / _SCATTER_FLOAT_ADD--> HBM
TC --tpu.enqueue_dma (DMA_MEMORY_ID_VMEM)-------> reads HBM into VMEM
sync: SC stream op carries a completion sflag; TC tpu.sem_wait on the same flag.
Channel 2 — VMEM-targeted DMA(快速,可编程): <-- THE SC->MXU FEED
SC --tpu_dma_hbm_to_vmem_sc_general (16 operands)-----------> TC VMEM
the destination sflag (TC side) AND the source DMA-done sflag (SC side)
are both carried in the operand vector.
Channel 3 — SFLAG-only control plane(无数据):
SCS --SetSyncFlag / AddSyncFlag / AtomicRemoteWriteSetDone--> shared SFLAG
TC --tpu.sem_wait / tpu.fetch_and_add_sync----------------> keyed on same SFLAG addr
remote address encoded per-gen by EncodeRemoteSyncFlagAddress{JfDf,Pufferfish,Viperfish}
and xla::ghostlite::dma_utils::EncodeRemoteSyncFlagAddress.
```text
### 规范 Embedding-Feed Fastpath
恰好有**一条**直接 SC→VMEM 路由,以及**一条** TC→SC-via-VMEM 路由 — 这是通过枚举反编译中全部 40 个 `sparse_core::tpu_dma_*` op 验证的,其中只有三个名字包含 `vmem`:
```text
tpu_dma_hbm_to_vmem_sc_general 16 operands ← SC produces, MXU consumes
tpu_dma_vmem_to_hbm_sc_general 16 operands ← MXU result writeback
tpu_dma_vmem_to_vmem_sc_general 16 operands ← VMEM→VMEM intra-TC因此规范 SC→MXU 路径是:
SC TEC -> TILE_SPMEM -> SPMEM -> HBM -> VMEM -> MXU (full aggregation)
SC TEC -> TILE_SPMEM -> HBM -> VMEM -> MXU (direct, skips SPMEM)
```text
当每个 tile 的结果直接供 MXU 消费时,短形式会跳过 SPMEM 聚合阶段;不存在 `spmem_to_vmem` 路由,因此驻留在 SPMEM 的 tile 在抵达 VMEM 前始终要经过 HBM。
> **NOTE —** VMEM 路由不存在 `simple` 和 `single_strided` 形式,这是结构性事实,不是遗漏:`*_to_vmem_sc_general` 和 `vmem_to_*_sc_general` **仅**以 `general`(16-operand)形式存在。SC↔TC VMEM transfer 始终携带完整 multi-dim DMA descriptor。重新实现者不能跨 SC↔TC 边界发出 “simple” 8-operand DMA — embedding tile 的 geometry(rows × embedding_dim,可能按 logical replica strided)始终需要完整 descriptor。
### 函数图
| Function | Address | Role |
|---|---|---|
| `LowerMemrefToMlo::lowerEnqueueDma` | `0x135105a0` | `tpu.enqueue_dma` → `sc_tpu.dma_{simple,general,single_strided}_start` + `sc_tpu.dma_wait` |
| `LowerMemrefToMlo::lowerEnqueueIndirectDma` | `0x13511da0` | `tpu.enqueue_indirect_dma` → indirect-stream ops |
| `LowerMemrefToMlo::lowerWaitDma` | `0x135135e0` | `tpu.wait_dma2` → `sc_tpu.dma_wait` |
| `LowerMemrefToMlo::getVerifiedDmaShapes` | `0x13509dc0` | 提取并验证 DMA shape |
| `LowerMemrefToMlo::checkShapeTileAlignment` | `0x135053a0` | 验证 cross-engine DMA 的 shape-tile alignment |
| `LowerMemrefToMlo::convertDeviceIdFromSubsliceToFullSlice` | `0x13505a40` | routing key 的 logical → physical core id |
---
## kStream vs kDma — 选择 Channel
### 目的
在 lowering transfer 之前,编译器将它分类为 *stream*(SC stream engine,高 random-access bandwidth,gather/scatter datapath)或 *DMA*(常规 DMA engine,高 contiguous bandwidth,cross-engine VMEM handoff)。SC→MXU feed 是 **kDma**:它是跨越 engine boundary 的 contiguous tile,不是 indexed gather。
### 分类器
```cpp
// xla::tpu::sparse_core::GetTransferKind @ 0x1351b140
// demangled tail "...MemorySpaceES8_bbbb" == (Target&, MemorySpace, MemorySpace, b, b, b, b)
TransferKind GetTransferKind(
const xla::jellyfish::Target& target,
mlir::sparse_core::MemorySpace src_mem,
mlir::sparse_core::MemorySpace dst_mem,
bool is_indirect,
bool is_atomic,
bool is_remote,
bool is_tile_scope); // -> TransferKind::kStream | TransferKind::kDma决策规则(来自 assertion-string anchor transfer_kind == TransferKind::kStream 和 == TransferKind::kDma,二者均出现在 lowering 反编译中):
| Transfer | Kind | 原因 |
|---|---|---|
| Indirect(gather / scatter),id-list driven | kStream | stream engine 具有更高 random-access bandwidth |
| Atomic(scatter-add into HBM) | kStream | MXU 做不了的 in-HBM FP-add |
| Embedding-style HBM traffic | kStream | single-row random access |
| Contiguous bulk transfer | kDma | sequential,常规 DMA engine |
| Cross-engine SC↔TC via VMEM | kDma | operand-feed tile 是 contiguous |
| Host(IOVA)transfer | kDma | host staging |
GOTCHA —
getTransferKind<EnqueueDMAOp>(0x135114a0)和getTransferKind<WaitDMA2Op>(0x135145e0)member-template 是按 op kind 划分的分类器,它们委托给自由函数GetTransferKind;随后 lowering 会 CHECK 所选 kind 与产生它的 source path 匹配(两个transfer_kind == TransferKind::k*assert)。把 VMEM-targeted transfer 发成kStream的重新实现会触发该 CHECK — SC↔TC VMEM feed 必须分类为kDma。两个 channel 还使用不同 HBM controller policy(SC stream 使用 random-access prefetch,TC DMA 使用 sequential-stream prefetch),所以分类不是表面装饰。
SFLAG 同步握手
目的
数据 channel 移动 byte;SFLAG channel 移动permission。因为 SC 和 TC 异步运行,MXU 在 SC 完成写入前不得 latch VMEM tile,SC 在 MXU 完成读取前不得覆盖 buffer。两个方向都是 sync flag — producer 递增、consumer 等待的小 counter。
SFLAG 内存模型
SFLAG 不是一个 register file;它在 sub-engine 层级切分成三个子空间,这由 verifier assertion 确认:一个 flag 的 memory space 必须恰好是以下三者之一:
sflag_memory_space == mlir::sparse_core::MemorySpace::sflag // global, cross-CORE (SC<->TC)
|| mlir::sparse_core::MemorySpace::sflag_tile // per-tile, TEC scope
|| mlir::sparse_core::MemorySpace::sflag_scs // per-SCS scope
```text
| MemorySpace | Scope | 是否跨到 TC? |
|---|---|---|
| `MemorySpace::sflag` | global SFLAG pool(cross-engine, cross-core) | **是** — 这是 SC↔TC 握手 flag |
| `MemorySpace::sflag_tile` | per-tile(TEC sub-engine scope) | 否 — SC 内部 |
| `MemorySpace::sflag_scs` | per-SCS(SCS sub-engine scope) | 否 — SC 内部 |
region scoping 由 verifier 强制,两个 assertion string 已在 `mlir::sparse_core::MloModuleVerifier::Verify(mlir::memref::StoreOp)`(`0x146a9da0`)中逐字节确认:
```text
"on smem_tile/sflag_tile is only allowed inside a tile_task or an execute sequencer function"
"on smem_scs/sflag_scs is not allowed inside a tile_task or an execute sequencer function"因此跨到 TC 的 flag 必须 位于全局 sflag 子空间。tile-scope 和 SCS-scope flag 被限制在各自 sub-engine 内部,即使只是在 SC sub-engine 之间共享也需要显式 address-space cast(tpu_addrspacecast_sflag_tile_sflag_scs 等),更不用说抵达 TC。
指向 SC Flag 的 TC 侧 Op
TC sequencer(TpuSequencer)发出四个参与握手的 mlir::tpu op。四个 symbol(SemaphoreWaitOp、SemaphoreSignalOp、SemaphoreReadOp、FetchAndAddSyncOp)都出现在反编译中:
| TC op | MLIR op | Operands | 作用 |
|---|---|---|---|
tpu.sem_wait | SemaphoreWaitOp | 2 | 等待 (sflag_ptr, threshold) — MXU-feed gate |
tpu.sem_signal | SemaphoreSignalOp | ≥2(variadic,AttrSizedOperandSegments) | 递增 flag,可选地指向 remote SC partition |
tpu.sem_read | SemaphoreReadOp | 1 → 1 result | 非阻塞读取 flag value |
tpu.fetch_and_add_sync | FetchAndAddSyncOp | 3(NOperands<3>)→ 1 result | atomic add,返回 old value(tile counters) |
tpu.enqueue_dma(EnqueueDMAOp)和 tpu.sem_signal(SemaphoreSignalOp)都携带一个 getRemoteDeviceAndSparseCoreIds<T>() specialization — 这确认了 DMA 和 semaphore 都能通过 DeviceAndCoreIds routing key 按 partition id 寻址 remote SparseCore。
expandTPUFetchAndAddSync helper(0x134e60c0,位于 ExpandTiledMemRefsPass)在目的 flag 位于 SC memory 时,将 TC 侧 tpu.fetch_and_add_sync lowering 为 sc_tpu.fetch_and_add — 即 SC 侧是 TC atomic-add primitive 的被动接收者。
握手序列
forward(SC produces, MXU consumes):
1. SC TEC vector-store completes the tile in VMEM (or HBM->VMEM DMA done).
2. SC sync_set / set_done_bit raises the completion flag in the global `sflag` subspace.
3. TC tpu.sem_wait(flag, threshold) advances; the MXU latches the tile (matprep.subr).
4. MXU runs; on result writeback, TC sem_signal the read-done flag.
5. SC sem_read / sync_wait sees the read-done flag and is free to reuse the buffer.
```text
当 pipeline 平衡时,wait 不会阻塞:SC tile N 的 `sync_set` 在 TC cycle T+1 完成,TC 的 `sem_wait` 在 T+1 立即解除阻塞,tile N 的 MXU latch 在 T+2 运行,而 SC tile N+1 的 gather 已经在 T+1 开始 — 因此当 MXU 请求 tile N+1 时,它已经驻留。
### Cross-Sub-Engine Sync(SC 内部,供给边界)
在 SC 能够升起 cross-CORE flag 之前,它自己的 sub-engine 必须就 tile 已完成达成一致。collective-offload emitter 通过 `OffloadFactory` 完成这点:
| Helper | Address | 作用 |
|---|---|---|
| `OffloadFactory::SyncScsWithTec(builder, value, CoreKind)` | `0x133e9260` | SCS 等待由 TEC 设置的 flag(函数体发出一个 `SyncWait`) |
| `OffloadFactory::SyncTecWithScs(builder, v1, v2, CoreKind)` | `0x133e8fe0` | TEC 等待由 SCS 设置的 flag |
| `OffloadFactory::AllocateSflag(builder, bool set, long count)` | `0x133e8320` | 分配 SFLAG region(count + initial state) |
| `OffloadFactory::StartLocalDma(...)` | `0x133eb3a0` | 带 src/completion sflag 的 same-chip DMA |
| `OffloadFactory::DmaWait(builder, sflag_idx, wait_size, ...)` | `0x133e8180` | 等待此前发出的 DMA |
`CoreKind` 参数化 helper 操作哪个 sub-engine:
| `CoreKind` | 含义 |
|---|---|
| `CoreKind::kScs` | SparseCore Scalar sub-engine |
| `CoreKind::kTec` | Tile-Execute sub-engine |
| `CoreKind::kTac` | Tile-Access sub-engine(仅 Viperfish / Ghostlite) |
> **NOTE —** 在 TPU7x(6acc60406,gfc namespace)上没有 TAC,因此 SCS↔TEC sync 折叠为单个 primitive:`sc_tpu.tile_wait_scs_smem`(`TileWaitScsSmemOp`,3 个 operand `= {sflag_ptr, expected_value, smem_buf}` — 已确认 `NOperands<3u>`)。Viperfish 和 Ghostlite 都仍保留完整 TAC ISA。TEC 等待 SCS 向 SMEM 写入指定值,然后读取 SMEM 并继续 — SCS 完成过去由 TAC 完成的地址计算。这是 TAC 替代 primitive;它不改变 *cross-CORE*(SC↔TC)握手,只改变 SC 内部 access→execute 同步。参见 [TAC Engine](tac-engine.md) 和下方按 generation 分节。
---
## Contracting-Depth 绑定
### 目的
一旦 SC tile 驻留在 VMEM 且 sync flag cleared,MXU 就会消费它。绑定问题是:*matmul 在哪个维度上 reduce,SC 的 sparsity 对 MXU 是否可见?* 答案是这个边界最清晰的部分:SC tile 作为普通 dense **moving operand** 进入 MXU,在 dense `MxuContractingSize` 上 reduce。MXU 从不看到“sparse” — sparsity 已经由上游 SC gather 完全解析。
### MXU Latch 的内容
MXU 侧运行标准 op family([MXU Slot](../isa/slot-mxu.md)):`vlatch` 加载 stationary weight tile,`vmatprep.subr` / `.mubr` 将 SC 产生的 VMEM tile 作为 moving operand 暂存到 `MATPUSH_TARGET_MSRA` / `MSRB`,`vmatmul` 驱动 systolic array,`vmatres` drain accumulator。activation tile 由 TC 的 `LoadActivationsChunk` path 从 VMEM 读取;embedded-lookup result *就是*该 activation tile。
```text
SC-produced VMEM tile --(matprep.subr/.mubr)--> MSRA/MSRB --(vmatmul, K steps)--> accumulator
^
dense MLP weights (vlatch, stationary)Contracting 常量
contracting depth 是 Target subclass 中按 codename 划分的 C++ literal — 不是 chip_parts 字段。逐字节精确值(归 SparseCoreTarget (Target+0x948) 所有,此处作为 consumer 复现):
| MXU constant | v3 Jelly | v4 Puffer | v5p Viperfish | v6e Ghostlite | TPU7x(6acc60406) | Source |
|---|---|---|---|---|---|---|
MxuContractingSize | 128 | 128 | 128 | 256 | 256 | base 0x1D490060; Ghostlite 0x1D497840 |
MxuNoncontractingSize | 128 | 128 | 128 | 256 | 256 | base 0x1D490080; Ghostlite 0x1D497860 |
MxuSparseContractingSize | 0 | 0 | 0 | 0 | 0 | base 0x1D4900C0; no override |
MxuContractingSizeIsDoubled(mode) | false | false | predicate | predicate | predicate | base 0x1D4900A0; VF 0x1D49AA60; GL 0x1D497880 |
重新实现者必须编码三件事:
- embedding-feed matmul 在 dense contracting dimension 上 reduce(
MxuContractingSize— Jellyfish 到 Viperfish 为 128,Ghostlite 类为 256,TPU7x/6acc60406 复用此值 — 没有独立的Tpu7xTarget;只有GhostliteTarget将MxuContractingSizeoverride 为 256)。reduction depth 是 embedding-dim(或其一个 tile),打包方式与任意其他 dense activation 完全相同。 MxuSparseContractingSize在每个 generation 上都是 0 — 没有任何Targetsubclass override 它。sparse-MXU contracting path 在此 build 中未使用。SC/MXU 的分工发生在 engine 层级(SC gather,MXU 做 dense matmul),而不是 MXU 内部 sparse contracting mode 层级。重新实现者不应为 embedding feed 寻找 sparse-MXU latch。- doubling predicate 与 sparsity 正交。
ViperfishTarget和GhostliteTarget都用相同 predicate(mode - 22) < 4uoverrideMxuContractingSizeIsDoubled— 对 rawGainLatchMode ∈ {22,23,24,25}为 true,即 4-bit packed-nibble(S4/U4)matmul mode,会把两个 nibble 打包到一个物理 systolic row 中(加倍有效 contracting depth)。这是 dense MXU 的 dtype-packing 功能;它只在 activation dtype 为 4-bit 时适用于 embedding-feed matmul,并不是 SC 专用路径。
GOTCHA — SC
SparseCoreLaneCount(Target::SparseCoreLaneCount,0x0F7906E0读取按 generation 的 target descriptor;运行时tensorflow::GetSparseCoreLaneCount可由 flag override,因此精确按 generation 的整数不是固定 literal)是 SC reduce 的 vector width,不是 MXU contracting depth。二者独立:SC lane count 是 TEC vector engine 对上游 gather/reduce 的 SIMD width;MXU contracting size(128 / 256)是下游 dense matmul reduce 的 systolic row depth。handoff 是 reshape boundary — SC 以自己的 lane geometry 产生[rows × embedding_dim]tile;MXU 将它重新切为 systolic array 的[contracting × noncontracting]。混淆二者会把 VMEM staging buffer 大小算错。参见 SparseCore Architecture 了解 SC lane geometry,参见 MXU Slot 了解 systolic tiling。
HLO 级边界标记
目的
握手在 HLO 层级创建:XlaSparseDenseMatmul custom-call 是唯一的 op,分解后变成 SC 侧 gather computation、TC 侧 dense matmul,以及二者之间的 SFLAG sync。下面是重新实现者的 front-end 必须发出以触发该边界的 op 名。
Custom Call
| HLO op | 含义 |
|---|---|
XlaSparseDenseMatmulOp(TF op-kernel)/ tf.XlaSparseDenseMatmulWithCsrInput | 单步 embedding lookup + dense matmul(基础边界标记) |
XlaSparseDenseMatmulGradOp(TF op-kernel) | backward:TC 计算 gradient,SC scatter-add 到 table |
tf.XlaSparseDenseMatmulWithCsrInput / …WithStaticBufferSize | 基础形式,使用 CSR(或 static-buffer-size)sparse-input format |
tf.XlaSparseDenseMatmulGradWith{Sgd,Adam,Ftrl,Adagrad,AdagradMomentum}AndCsrInput[Op] | backward + optimizer fused |
SparseDenseMatmulWithMinibatchingOp(internal HLO) | mini-batch variant(把 batch 细分为适合 TILE_SPMEM 的 window);由 sparse-dense-matmul-with-minibatching-op-decomposer 分解 |
tf.XlaSparseDenseMatmulCustomCombinerOnTc[Grad]With…CsrInput / SparseDenseMatmulCustomCombinerTcCombiner{,Megachip}Op | TC 侧 custom combiner(以及 mega-chip cross-chip 形式) |
GatherMulScatterSparseDenseMatmulOp(internal HLO) | gather-mul-scatter fused form |
XlaLocalSparseDenseMatmulOp(TF op-kernel)/ tf.XlaLocalSparseDenseMatmul | single-device variant(无 SPMD partitioning) |
每个 op 都由其 decomposer(sparse_dense_matmul_* family)重写成 {SC-side gather computation, TC-side dense matmul, cross-engine SFLAG sync} tuple。EmbeddingsPass / EmbeddingBackwardPass 识别 embedding-lookup HLO pattern 并产生此 custom-call;EmbeddingDataFormattingDecomposer 在 SC computation 上设置 sc.core_type = "sparse"。分解之后,SC half 由 SparseCoreHierarchicalSpmdPartitioner 分区(PadSparseCoreProgramInputs / UnPadSparseCoreProgramOutputs 对齐 shard boundary),并交给 SC-MLO pipeline。参见 SC Backend Pipeline 了解 pass sequence,参见 SparseCore vs Neuron MatmultSparse 了解 cross-vendor comparison。
NOTE — SC↔TC boundary marker 是 HLO custom-call,不是 MLIR op。程序抵达
sc_tpu.*(SC mid-level IR)时,两个 half 已经是独立 computation;唯一将它们绑定起来的是边界处插入的 SFLAG-keyed sync。MLIR 层级的重新实现者看到的是两个独立 program — coupling 是 sync-flag use-def chain,SyncFlagVerifierPass会验证它。
Partition Assignment — 哪个 SC 供给哪个 MXU
目的
一个 chip 有多个 SC 和较少的 TC;每个 TC 侧 DMA / semaphore 上的 routing key 选择它寻址哪个 SC partition。该比例按 generation 固定。
比例
核心常量是 xla::jellyfish::lowering_util::SparseCoreCountPerTensorCore(tpu::TpuTopology const*)(0x1C6CB760)= sparse_core_count_per_chip / tensor_core_count_per_chip,由两个 CHECK 保护 — "sparse_core_count_per_chip >= tensor_core_count_per_chip" 和 "sparse_core_count_per_chip % tensor_core_count_per_chip == 0"(整数 4:1 比例):
| Gen | TCs/chip | SCs/chip | 每个 TC 的 SC 数 |
|---|---|---|---|
| Viperfish | 2 | 8 | 4 |
| Ghostlite | 2 | 8 | 4 |
| TPU7x(6acc60406) | 1 | 4 | 4 |
4-SCs-per-TC 比例在所有带 SC 的 generation 中保持不变。四个 SC 共同产生由一个 MXU sequence 消费的 embedding-tile row;每个 SC 的 read_register_sparse_core_id(SCS scalar opcode)返回其 per-chip id,而 IfLocalSparseCore(0x1C6CD000)发出一个 conditional,仅当当前 SC id 匹配指定 target 时运行(SPMD-replicated SC kernel 中的 per-partition gate)。
每个 tpu.enqueue_dma / tpu.sem_signal 上的 TC 侧 DeviceAndCoreIds struct((logical_device_id, logical_core_id) pair)选择 SC;convertDeviceIdFromSubsliceToFullSlice(0x13505a40)将 logical id 映射到 physical id,dialect-level LogicalToPhysicalDeviceIdPass 在 lowering 到 Mlo 前执行转换。
GOTCHA — 规范赋值(SC 0→MXU lane 0、SC 1→lane 1,……)是一个约定,不是硬连线 — 它是编译器填入的
DeviceAndCoreIds值。重新实现者必须显式填充 routing key;硬件中不存在隐式 SC↔MXU affinity。Jellyfish / Dragonfish / Pufferfish generation 没有 SparseCore(presence gateTarget::SupportsSparseCore返回 false),因此整个边界在它们上不存在。
按 Generation 的差异
| Aspect | Viperfish(vfc) | Ghostlite(glc) | TPU7x / 6acc60406(gfc) |
|---|---|---|---|
| TAC sub-engine | yes | yes | no |
| SC-internal access→execute sync | SCS→TAC→TEC three-way | SCS→TAC→TEC three-way | SCS→TEC two-way via tile_wait_scs_smem |
| SCs per chip / per TC | 8 / 4 | 8 / 4 | 4 / 4 |
| DMA channels | 8 SC + 12 TC | 8 SC + 12 TC | 4 SC + 12 TC |
MxuContractingSize | 128 | 256 | 256(复用 GhostliteTarget) |
| Remote-sflag encoder | EncodeRemoteSyncFlagAddressViperfish | xla::ghostlite::dma_utils::EncodeRemoteSyncFlagAddress | same Ghostlite helper |
Dual-channel sync(AddBothSyncFlag …) | yes | yes | no |
| Yieldable sync family | 12 ops | 12 ops | no |
cross-CORE 握手协议在三个 SC generation 中相同 — 全局 sflag、tpu.sem_wait、double-buffer。差异是:(1) gfc generation(TPU7x / 6acc60406)去掉 TAC,并把它的 address/DMA-issue 角色折入 TEC,用 tile_wait_scs_smem 替换 three-way SC-internal sync;(2) Viperfish 和 Ghostlite 都携带 dual-channel sync family(Add/SetBothSyncFlag、Add/SetOtherSyncFlag)以及 12 个 yieldable-sync op(SparseCoreScalarMisc_YieldableSync{Done,NotDone,Equal,NotEqual,Greater,Less,…}),二者在 gfc 中都被移除;(3) remote-SFLAG-address bit-encoding 按 generation 划分,并通过 keyed on TpuVersion 的 RemoteSyncFlagEncoderRegistry 分发。
NOTE —
CoreKind::kTac在 TPU7x 上仍是注册 enum value,用于 binary compatibility,但 gfc emitter 从不产生它 — 这由零个gfc::isa::SparseCoreTac*symbol 确认(与vfc::isa::SparseCoreTac*和glc::isa::SparseCoreTac*中完整 TAC ISA surface 相对)。面向 TPU7x 的重新实现者必须通过 TEC +tile_wait_scs_smem路由 gather-issue 和 access→execute sync,而不是通过 TAC path。
边界处的 Bandwidth 和 Back-Pressure
SC↔TC 和 SC↔HBM traffic 共享 on-chip HBM controller,并按 credit 仲裁。相关 primitive:
| Mechanism | Symbol / op | 作用 |
|---|---|---|
| Per-channel DMA credit | sc_tpu.set_dma_credit | TC 有 12 个 DMA channel,SC 有 4 个;credit 防止垄断 HBM bus |
| Throttle on sflag range | sc_tpu.set_dma_throttle_sflag_range | HBM 饱和时对 SC stream engine 施加 back-pressure |
| Cost-model bandwidth share | backend_config_util::GetSparseCoreHbmBandwidthAdjustmentFactor | 每个 HLO op 的 fractional HBM-bandwidth budget |
| Concurrent-offload limit | FLAGS_xla_tpu_sparse_core_offload_queuing_overlap_limit | 最大 SC-offload in-flight task 数(按 generation 调优) |
| Channel policy | chip-config protos(*_chip_configs_legacy_sparse_core.binarypb) | SC 使用 random-access prefetch,TC 使用 sequential-stream prefetch |
两个引擎在同一 DRAM 上使用不同 HBM MMU page-prefetcher policy — SC 使用 random-access(single-row gather),TC 使用 sequential-stream(contiguous tile fetch)。这就是 kStream / kDma 分类为何不止影响 bundle slot:它还选择 HBM channel policy。
边界处的错误处理
| Failure | Mechanism | Detail |
|---|---|---|
| MXU starvation(TC waits for SC) | tpu.sem_wait finite timeout | FLAGS_xla_tpu_debug_sc_sflag_wait_timeout_ms;timeout 时 TC 发出 controlled halt,并写入命名 source HLO instruction 的 diagnostic record |
| SC stall | SCS Halt opcode | 带 status code 的 controlled halt;无 interrupt — host 轮询 sequencer status register;SDC checker 继续运行 |
| Sync-flag use-def error | SyncFlagVerifierPass(compile-time) | 每个 wait 必须有匹配 producer;每个 producer 必须为 ≥1 个 consumer 计数一个 threshold |
| Wait-stat telemetry | SflagWaitInstrumentationPass | 在 SC↔TC boundary 插入 wait-statistics counter(xla_tpu_collect_sflag_wait_stats_filter) |
| SDC mismatch | TPU_MEMORY_RESERVATION_TYPE_SPARSE_CORE_SDC_CHECKER_REPORT_SYNC_FLAG | SDC checker 在 row-checksum mismatch 时设置的专用 SFLAG range |
verifier 可通过 sc.disable_before_use_sync_flag_verification / sc.disable_after_use_sync_flag_verification HLO attribute 按 kernel opt out(用于手写 kernel)。xla_sc_conservative_sflag_aliasing 强制 verifier 禁止任何 SFLAG-range aliasing — 这是调试 stuck pipeline 的模式。
限制与 Open Item
| Item | Status |
|---|---|
40 个 tpu_dma_*_sc_* op;只有 3 个名字包含 vmem;仅 general 形式 | 已在反编译中枚举 |
GetTransferKind signature (Target&, MemorySpace, MemorySpace, 4×bool) | demangled symbol + kStream/kDma assert |
| SFLAG 3-subspace model + region-scoping assertion | assertion string 已读取 |
TC-side Semaphore{Wait,Signal,Read}Op + FetchAndAddSyncOp | symbol 存在 |
SyncScsWithTec / SyncTecWithScs / AllocateSflag(.,false,2) double-buffer | 已定位函数体;函数体中有 SyncWait |
MxuContractingSize 128/256;MxuSparseContractingSize = 0 | 按 codename 的 literal(见 target descriptor) |
DeviceAndCoreIds byte layout(routing key) | 恢复为 optional<>;field width 未分解 |
| 按 generation 的 SFLAG remote-address bit-encoding | encoder 已定位;bit composition 未解码 |
EmbeddingsPassType enum value list | GetLogicalReplicaInfo 的参数;value 未以 string 暴露 |
TileTaskOp access-vs-execute region content rule | op 存在(MloModuleVerifier::Verify(TileTaskOp) 0x146aca00);精确 region count 和 per-region legality predicate 未清晰反编译 |
精确 EnqueueDMAOp::getPriority() value space | getter 存在;arbitration rule 未追踪 |
交叉引用
- SparseCore Architecture — SC geometry、四层 memory model,以及读取 contracting binding 的
SparseCoreTarget(Target+0x948)。 - Stream Gather/Scatter — SC 内部 indirect-DMA datapath(
IndirectStreamslot、逐元素地址公式),它产生本页交给 MXU 的 tile。 - MXU Slot — TC MXU op family(
vlatch/vmatprep/vmatmul/vmatres)、128×128 systolic array,以及本页 contracting binding 供给的GainLatchModedoubling mode。 - MATPREP / IAR Latch Slot — SC 产生的 VMEM tile 进入的 moving-operand staging slot。
- SparseCoreTarget (
Target+0x948) — 本页消费的逐字节精确MxuContractingSize/MxuSparseContractingSize/ doubling-predicate 表。 - SC Backend Pipeline — 构建边界两半的 SC-MLO pass pipeline(以及 HLO
sparse_dense_matmul_*decomposer)。 - SC Core Selection — computation 如何分配到物理 SparseCore partition(
DeviceAndCoreIdsrouting)。 - getSequencerType — 选择发出每次 transfer 的 engine 的 SCS/TAC/TEC selection。
- TAC Engine — TPU7x(6acc60406)上缺失的 Tile-Access sub-engine;
tile_wait_scs_smem替代项。 - SparseCore vs Neuron MatmultSparse — gather-then-matmul boundary 的 cross-vendor comparison。
- SparseCore Overview — Part IX 的导航入口。
- Binary:
extracted/libtpu-0.0.40-cp314-cp314-manylinux_2_31_x86_64/libtpu/libtpu.so(build-id89edbbe81c5b328a958fe628a9f2207d) - 索引项: Part IX — SparseCore & BarnaCore / SparseCore cross-cutting — 返回索引