Skip to content

SC ↔ MXU 握手

本页中的每个 op 名、symbol、operand 数量、assertion string 和 literal,均从 libtpu-0.0.40-cp314 wheel(BuildID md5 89edbbe81c5b328a958fe628a9f2207d;build libtpu_lts_20260413_b_RC00,LLVM SHA 8918319853fbdf9e6f6cb69e96848f913a22bc31)里的 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_waitSemaphoreWaitOp,2 个 operand)在该 flag 的 writer count 达到阈值时前进。该 flag 位于用于 cross-CORE 协调的全局 sflag 子空间;cross-sub-engine flag(sflag_scssflag_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 writebackmlir::sparse_core::tpu_dma_vmem_to_hbm_sc_general(16 operands)
DMA loweringLowerMemrefToMlo::lowerEnqueueDma0x135105a0)→ sc_tpu.dma_general_start + sc_tpu.dma_wait
kStream vs kDmaxla::tpu::sparse_core::GetTransferKind0x1351b140(Target&, src, dst, 4×bool)
TC 侧同步 optpu.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 syncOffloadFactory::SyncScsWithTec0x133e9260)/ SyncTecWithScs0x133e8fe0
MXU contracting depthTarget::MxuContractingSize = 128 base / 256 Ghostlite class;MxuSparseContractingSize = 0(未使用)
Double-bufferOffloadFactory::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

text
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 路径是:

text
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 反编译中):

TransferKind原因
Indirect(gather / scatter),id-list drivenkStreamstream engine 具有更高 random-access bandwidth
Atomic(scatter-add into HBM)kStreamMXU 做不了的 in-HBM FP-add
Embedding-style HBM traffickStreamsingle-row random access
Contiguous bulk transferkDmasequential,常规 DMA engine
Cross-engine SC↔TC via VMEMkDmaoperand-feed tile 是 contiguous
Host(IOVA)transferkDmahost 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 必须恰好是以下三者之一:

text
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(SemaphoreWaitOpSemaphoreSignalOpSemaphoreReadOpFetchAndAddSyncOp)都出现在反编译中:

TC opMLIR opOperands作用
tpu.sem_waitSemaphoreWaitOp2等待 (sflag_ptr, threshold) — MXU-feed gate
tpu.sem_signalSemaphoreSignalOp≥2(variadic,AttrSizedOperandSegments递增 flag,可选地指向 remote SC partition
tpu.sem_readSemaphoreReadOp1 → 1 result非阻塞读取 flag value
tpu.fetch_and_add_syncFetchAndAddSyncOp3(NOperands<3>)→ 1 resultatomic add,返回 old value(tile counters)

tpu.enqueue_dmaEnqueueDMAOp)和 tpu.sem_signalSemaphoreSignalOp)都携带一个 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 的被动接收者。

握手序列

text
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 constantv3 Jellyv4 Pufferv5p Viperfishv6e GhostliteTPU7x(6acc60406)Source
MxuContractingSize128128128256256base 0x1D490060; Ghostlite 0x1D497840
MxuNoncontractingSize128128128256256base 0x1D490080; Ghostlite 0x1D497860
MxuSparseContractingSize00000base 0x1D4900C0; no override
MxuContractingSizeIsDoubled(mode)falsefalsepredicatepredicatepredicatebase 0x1D4900A0; VF 0x1D49AA60; GL 0x1D497880

重新实现者必须编码三件事:

  1. embedding-feed matmul 在 dense contracting dimension 上 reduceMxuContractingSize — Jellyfish 到 Viperfish 为 128,Ghostlite 类为 256,TPU7x/6acc60406 复用此值 — 没有独立的 Tpu7xTarget;只有 GhostliteTargetMxuContractingSize override 为 256)。reduction depth 是 embedding-dim(或其一个 tile),打包方式与任意其他 dense activation 完全相同。
  2. MxuSparseContractingSize 在每个 generation 上都是 0 — 没有任何 Target subclass override 它。sparse-MXU contracting path 在此 build 中未使用。SC/MXU 的分工发生在 engine 层级(SC gather,MXU 做 dense matmul),而不是 MXU 内部 sparse contracting mode 层级。重新实现者不应为 embedding feed 寻找 sparse-MXU latch。
  3. doubling predicate 与 sparsity 正交。 ViperfishTargetGhostliteTarget 都用相同 predicate (mode - 22) < 4u override MxuContractingSizeIsDoubled — 对 raw GainLatchMode ∈ {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 SparseCoreLaneCountTarget::SparseCoreLaneCount0x0F7906E0 读取按 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}OpTC 侧 custom combiner(以及 mega-chip cross-chip 形式)
GatherMulScatterSparseDenseMatmulOp(internal HLO)gather-mul-scatter fused form
XlaLocalSparseDenseMatmulOp(TF op-kernel)/ tf.XlaLocalSparseDenseMatmulsingle-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 比例):

GenTCs/chipSCs/chip每个 TC 的 SC 数
Viperfish284
Ghostlite284
TPU7x(6acc60406)144

4-SCs-per-TC 比例在所有带 SC 的 generation 中保持不变。四个 SC 共同产生由一个 MXU sequence 消费的 embedding-tile row;每个 SC 的 read_register_sparse_core_id(SCS scalar opcode)返回其 per-chip id,而 IfLocalSparseCore0x1C6CD000)发出一个 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;convertDeviceIdFromSubsliceToFullSlice0x13505a40)将 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 gate Target::SupportsSparseCore 返回 false),因此整个边界在它们上不存在。


按 Generation 的差异

AspectViperfish(vfc)Ghostlite(glc)TPU7x / 6acc60406(gfc)
TAC sub-engineyesyesno
SC-internal access→execute syncSCS→TAC→TEC three-waySCS→TAC→TEC three-waySCS→TEC two-way via tile_wait_scs_smem
SCs per chip / per TC8 / 48 / 44 / 4
DMA channels8 SC + 12 TC8 SC + 12 TC4 SC + 12 TC
MxuContractingSize128256256(复用 GhostliteTarget
Remote-sflag encoderEncodeRemoteSyncFlagAddressViperfishxla::ghostlite::dma_utils::EncodeRemoteSyncFlagAddresssame Ghostlite helper
Dual-channel sync(AddBothSyncFlag …)yesyesno
Yieldable sync family12 ops12 opsno

cross-CORE 握手协议在三个 SC generation 中相同 — 全局 sflagtpu.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/SetBothSyncFlagAdd/SetOtherSyncFlag)以及 12 个 yieldable-sync op(SparseCoreScalarMisc_YieldableSync{Done,NotDone,Equal,NotEqual,Greater,Less,…}),二者在 gfc 中都被移除;(3) remote-SFLAG-address bit-encoding 按 generation 划分,并通过 keyed on TpuVersionRemoteSyncFlagEncoderRegistry 分发。

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:

MechanismSymbol / op作用
Per-channel DMA creditsc_tpu.set_dma_creditTC 有 12 个 DMA channel,SC 有 4 个;credit 防止垄断 HBM bus
Throttle on sflag rangesc_tpu.set_dma_throttle_sflag_rangeHBM 饱和时对 SC stream engine 施加 back-pressure
Cost-model bandwidth sharebackend_config_util::GetSparseCoreHbmBandwidthAdjustmentFactor每个 HLO op 的 fractional HBM-bandwidth budget
Concurrent-offload limitFLAGS_xla_tpu_sparse_core_offload_queuing_overlap_limit最大 SC-offload in-flight task 数(按 generation 调优)
Channel policychip-config protos(*_chip_configs_legacy_sparse_core.binarypbSC 使用 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。


边界处的错误处理

FailureMechanismDetail
MXU starvation(TC waits for SC)tpu.sem_wait finite timeoutFLAGS_xla_tpu_debug_sc_sflag_wait_timeout_ms;timeout 时 TC 发出 controlled halt,并写入命名 source HLO instruction 的 diagnostic record
SC stallSCS Halt opcode带 status code 的 controlled halt;无 interrupt — host 轮询 sequencer status register;SDC checker 继续运行
Sync-flag use-def errorSyncFlagVerifierPass(compile-time)每个 wait 必须有匹配 producer;每个 producer 必须为 ≥1 个 consumer 计数一个 threshold
Wait-stat telemetrySflagWaitInstrumentationPass在 SC↔TC boundary 插入 wait-statistics counter(xla_tpu_collect_sflag_wait_stats_filter
SDC mismatchTPU_MEMORY_RESERVATION_TYPE_SPARSE_CORE_SDC_CHECKER_REPORT_SYNC_FLAGSDC 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

ItemStatus
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 assertionassertion string 已读取
TC-side Semaphore{Wait,Signal,Read}Op + FetchAndAddSyncOpsymbol 存在
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-encodingencoder 已定位;bit composition 未解码
EmbeddingsPassType enum value listGetLogicalReplicaInfo 的参数;value 未以 string 暴露
TileTaskOp access-vs-execute region content ruleop 存在(MloModuleVerifier::Verify(TileTaskOp) 0x146aca00);精确 region count 和 per-region legality predicate 未清晰反编译
精确 EnqueueDMAOp::getPriority() value spacegetter 存在;arbitration rule 未追踪

交叉引用

  • SparseCore Architecture — SC geometry、四层 memory model,以及读取 contracting binding 的 SparseCoreTargetTarget+0x948)。
  • Stream Gather/Scatter — SC 内部 indirect-DMA datapath(IndirectStream slot、逐元素地址公式),它产生本页交给 MXU 的 tile。
  • MXU Slot — TC MXU op family(vlatch / vmatprep / vmatmul / vmatres)、128×128 systolic array,以及本页 contracting binding 供给的 GainLatchMode doubling 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(DeviceAndCoreIds routing)。
  • 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-id 89edbbe81c5b328a958fe628a9f2207d
  • 索引项: Part IX — SparseCore & BarnaCore / SparseCore cross-cutting — 返回索引