Skip to content

SCS(标量)引擎

本页中的每个地址、偏移、opcode 值和 bundle 位位置,均从 libtpu-0.0.40-cp314 wheel(build-id 89edbbe81c5b328a958fe628a9f2207d)中的 libtpu.so 逐字节读取而来。其他版本会有所不同。

摘要

SCS,即 SparseCore 标量定序器,是 SparseCore 的控制 CPU。TEC 是负责归约 embedding tile 的宽向量引擎,TAC 是发出 tile-fetch DMA 的引擎,而 SCS 是运行程序计数器、计算 gather/scatter 地址、管理环形缓冲区、读取芯片硬件寄存器(GTC 时钟、tile id、sparse-core id、DMA credit),并发出 sync-flag 和原子操作以协调各个 SC tile 及其与 TensorCore 之间关系的标量机器。它是三个 SparseCore 引擎中最窄的一个,相比 TAC/TEC 的 64 字节 bundle,SCS 使用 32 字节(256 位)VLIW bundle;也是唯一一个 bundle 布局在三代 SparseCore(Viperfish、Ghostlite、6acc60406)之间都保持逐字节一致的引擎。最接近的常见类比是一个驱动协处理器的小型顺序标量核:SCS 是宿主循环,而启动进入 TEC 的 TileTaskOp/LaunchTileTaskOp 就是它的“kernel launch”。

SCS bundle 是由通用小端 bit packer 打包的一组固定 slot。每个 slot 由专用的 <Slot>Encoder::Encode 编码,codec 分派器(SparseCoreScsCodecBase::Encode,gfc 0x1391ef60)依次调用这些编码器,并把同一个输出缓冲区 Span 交给每个 slot 编码器,因此每个 slot 都写入相对于 bundle 的绝对 bit 位置。该 bundle 没有校验尾标(不同于 TensorCore 的 0x55 字节):全零 bundle 表示“所有 slot 均未激活”。在 7 位保留前缀之后,是四个 20 位立即数 slot、一个 24 位 scalar→vector 桥,以及三个 27 位标量 slot:ScsScalarMiscScalarAlu1ScalarAlu0,它们的 opcode 字段分别位于 bundle bit 127、154 和 181。DMA 与 Stream 的“slot”并不是独立物理区域;它们是某条标量 lane 的 oneof 替代形式,复用同一个 opcode 字段,并借用较低的 payload 区域放置描述符。

SCS 指令集是两级 opcode 空间。6 位 primary opcode 要么选择一个具体 op,要么选择一个 op 类别;op 类别(Sync、SyncWatch、Atomic、control、register-read、config-set)随后携带 secondary sub-opcode 字段来选择精确操作。两个 ALU lane(ScalarAlu0/ScalarAlu1)共享同一个 opcode 命名空间,只在 bundle bit 位置和少数 lane 专属 op 上不同;ScsScalarMisc 承载 atomic + sync-flag 族以及一个 integer-ALU 子集(无 FP、无 branch)。本页记录 bundle 字节布局、每个 slot 的 bit base、27 位标量 slot 模板、标量 opcode 清单(primary + 四种 escape 编码),以及 SCS 如何通过 LaunchTileTaskOp 和共享 sync-flag 池向 TAC/TEC 引擎发出工作。

对于重新实现,契约如下:

  • 32 字节 bundle,带绝对的 per-slot bit base,没有校验尾标。 打包四个 20 位立即数(@7/27/47/67)、一个 24 位 VectorScalar 桥(@87),然后是位于 base 111/138/165 的三个 27 位标量 slot Misc/Alu1/Alu0。bit 0..6192..255 不由任何 slot 编码器写入。此布局在 VF/GL/GF 上固定。
  • 27 位标量 slot 模板。 相对 slot:x0 @+0/5、ScalarY @+5/6、x1 @+11/5OPCODE @+16/6、predication 头 @+22(3 位 normal / 4 位 rotate 重叠 + 1 位 inversion + 1 位 is-rotate)。三个标量 slot 以及 Dma/Stream 的 scalar-lane 部分都完全相同。
  • 两级 opcode 空间。 6 位 primary opcode 是具体 op(例如 IntegerAdd=0x0a类别 escape:control op 使用 11 位字段,register read 使用 17 位字段(ReadRegister* = 0x280..0x28d),config set 使用 16 位字段(Set* = 0x4001..0x4005),divide-push 使用 0x16xxxx 字段。Alu0 与 Alu1 共享这些值;只有 bundle bit 位置不同。
  • issue 模型按 attribute 启动,而不是按 op 启动。 SCS 不在每条指令中携带数值型 engine selector。编译器把 TEC 主体 outline 成标记 sc.sequencer="execute"func.func,保留标记为 "scs" 的外层 SCS 控制程序,并在 lowering 时根据该字符串选择 per-engine codec 模板(TpuSequencerType = 3 SCS / 4 TAC / 5 TEC)。
引擎SparseCore 标量定序器(控制 / 寻址 / 同步)
Sequencer enumTpuSequencerType = 3 (TPU_SEQUENCER_TYPE_SPARSE_CORE_SEQUENCER)
Bundle 大小32 字节 / 256 位;没有 0x55 校验尾标
Codec 根SparseCoreScsCodecBase(per-gen vxc.vfc / gxc.glc / gxc.gfc);gfc Encode 0x1391ef60
Bit packerBitCopy(dst, dst_bitoff, src, src_bitoff, nbits) 0x1fa0a900(小端)
标量 slotScsScalarMisc(base 111,op @127) · ScalarAlu1(base 138,op @154) · ScalarAlu0(base 165,op @181
标量 slot 宽度27 位;6 位 primary opcode @+16
Opcode 数量(gfc)ScalarAlu 95(并集;Alu0 78 / Alu1 82) · ScsScalarMisc 82 · Dma 3 种形式 · Stream 3 种形式 · Immediates 1 · VectorScalar 1
跨代布局Bundle + slot base 在 VF / GL / GF 上逐字节一致

SparseCore 中的角色与位置

用途

SCS 是 SparseCore 的标量控制平面。在 embedding 数据路径(Architecture)中,它把 lookup index 流转换成驱动 SparseCore 其余部分所需的地址、同步事件和 tile-task launch。具体来说,它会运行程序计数器并执行 branch/call;用整数 ALU 计算 HBM[table_base + index_i × row_stride] gather 地址;在 SMEM 中暂存地址/描述符缓冲区;管理贯穿 tile window 的环形缓冲寄存器(CBREG);读取控制前进条件的硬件寄存器(GTC 时钟、DMA-credit 寄存器、fence 状态、tile/sparse-core id);并发出 sync-flag 和原子操作,让 SC tile 彼此之间以及与 TensorCore 之间完成握手。它不拥有向量路径,也不负责 tile load/store,那是 TEC 的工作。SCS 是循环;TEC 是循环体。

NOTE — SCS 是持久化的“外层”程序;TEC 主体会被 outline。 编译器通过把每个 sc_tpu.tile_task region outline 到自己的 func.func(标记 sc.sequencer="execute",即 TEC)中,并在外层函数中用 LaunchTileTaskOp 替换它,从而切分 SC 计算;外层函数保持 sc.sequencer="scs"。因此 SCS 程序是发出 launch 的控制循环;per-tile 计算位于被启动的 TEC 函数中。参见 Region → Sequencer Outliner

引擎清单

每一代搭载 SparseCore 的芯片都有 SCS。会变化的是 TAC:Viperfish 和 Ghostlite 上存在,6acc60406 上被移除(tile-fetch issue 折叠进 SCS+TEC 组合)。SCS 本身保持不变。Marketing-name / codename / family-namespace 映射遵循同级文档 SparseCore Overview

MarketingCodenameFamily nsSCSbundle备注
TPU v5pViperfishvxc.vfcY32 B首个三引擎拆分 SCS+TAC+TEC
TPU v6eGhostlitegxc.glcY32 B完整 SCS+TAC+TEC;TEC 加宽
TPU7x6acc60406gxc.gfcY32 B无 TAC;SCS 增加 rotating-preg op

NOTE — 两个 enum 空间都会给 SparseCore sequencer 编号,但它们相差一(proto-vs-C++)。 C++ tpu::TpuSequencerType enum 编号为 SCS=3 / TAC=4 / TEC=5,这由 demangled EncoderBase<… SparseCoreScsCodecBase …, LN3tpu16TpuSequencerTypeE3E> 符号确认(3E 后缀就是字面值 3);同样的 {3,4,5} 编号也是 TpuSequencerTypeToString 的渲染结果(off_22010DE0[3]="SparseCoreSequencer")、codec-metadata 表采用的值,以及 TpuCoreParts::SequencerParts 的索引值(参见 Architecture,geometry 在 codec-enum TEC=5 处读取,因此 SCS=3,而不是 4)。所有 C++ 侧使用都共享这一套编号。相差一的对应项是 protobuf enum TpuSequencerTypeProto,它保留 INVALID=0,因此编号为 SCS=4 / TAC=5 / TEC=6;当 proto 值进入 C++ enum 时,TpuSequencerTypeFromProto 会减一。对所有 encoder、codec 和 core-parts index 使用 {3,4,5};只有原始 TpuSequencerTypeProto 字段携带 {4,5,6}。不要混用二者。


SCS Bundle(32 字节)

布局

bundle 为 256 位。没有 slot 编码器写入 bit 7 以下或 bit 191 以上;bit 0..6 是保留/header 前缀,192..255 是 padding。字节布局是从每个 per-slot 编码器内部 BitCopy 的 destination-bit 立即数恢复的(分派器把同一个缓冲区 Span 传给每个编码器,因此 bit offset 是绝对的),并且在 Viperfish、Ghostlite、6acc60406 上逐字节一致。

text
SCS bundle — 32 bytes / 256 bits (VF / GL / GF identical)
bit:  0      7                 87        111       138       165       192        255
      ┌──────┬──────────────────┬─────────┬─────────┬─────────┬─────────┬──────────┐
      │ rsvd │ ScalarImmediates │ Vector  │ ScsScal │ ScalarA │ ScalarA │ reserved │
      │ 7b   │ 4 × 20-bit       │ Scalar  │ arMisc  │ lu1     │ lu0     │ / pad    │
      │ hdr  │ @7,@27,@47,@67   │ bridge  │ op@127  │ op@154  │ op@181  │ 64 bits  │
      └──────┴──────────────────┴─────────┴─────────┴─────────┴─────────┴──────────┘
                                  24-bit    27-bit    27-bit    27-bit
      Dma   (oneof of a scalar lane): opcode @181/@154, descriptor payload @87..142
      Stream(oneof of a scalar lane): opcode @181/@154, descriptor payload @99..142
```text

| Slot | Base | End | 宽度 | Opcode bit | 内部模板 |
|---|---:|---:|---:|---:|---|
| (保留 header) | 0 | 6 | 7 | — | bundle 前缀;含义未解码 |
| `ScalarImmediates` | 7 | 86 | 80 | — | 4 × 20 位(`@7,@27,@47,@67`) |
| `VectorScalar` | 87 | 110 | 24 | — | scalar→vector 桥(4 × {5-bit,1-bit} pair) |
| `ScsScalarMisc` | 111 | 137 | 27 | 127 | 27 位标量模板 |
| `ScalarAlu1`(lane 1) | 138 | 164 | 27 | 154 | 27 位标量模板 |
| `ScalarAlu0`(lane 0) | 165 | 191 | 27 | 181 | 27 位标量模板 |
| (保留 / pad) | 192 | 255 | 64 | — | 不由任何 slot 编码器写入 |
| `Dma`(lane 的 oneof) | 87 | 191 | — | 181 / 154 | 标量 opcode + payload `@87..142` |
| `Stream`(lane 的 oneof) | 99 | 191 | — | 181 / 154 | 标量 opcode + payload `@99..142` |

> **QUIRK — 不存在独立的“DMA slot”或“Stream slot”。** SparseCore DMA 或 Stream 指令是标量 lane 的一种 `oneof` *形式*:它把 opcode 写入该 lane 的 6 位 opcode 字段(lane 0 为 `@181`,lane 1 为 `@154`),并把多 word 描述符溢写到较低的 payload 区域(immediate / VectorScalar / Misc 区域,bit `87..142`)。这就是此前 decode 工作报告 Stream opcode “位于 bit 181”的原因:那是*绝对*的 scalar-lane-0 opcode base,而不是 Stream 专属位置。若重新实现者分配物理上独立的 DMA/Stream 区域,将错误估算 bundle 大小并重复占用 bit `87..142`。

### 编码器分派与共享缓冲区

codec 分派器是一个很薄的循环,会依次调用每个成员 slot 编码器,并把*同一个*输出缓冲区交给它们。反编译的 gfc `SparseCoreScsCodecBase::Encode`(`0x1391ef60`)调用每个 `<Slot>::Encode((char*)bundle + member_off, msg, a3 /*buf.ptr*/, a4 /*buf.len*/)`;`a3,a4` 这一对(目标缓冲区 span)在每次调用中都保持不变,只有 `rdi` 成员编码器指针不同。随后每个编码器用通用 LE packer `BitCopy(dst, dst_bitoff, src, src_bitoff, nbits)`(`0x1fa0a900`)打包自己的字段。因此 `dst_bitoff` 立即数就是绝对 bundle bit。

```c
function SparseCoreScsCodecBase_Encode(bundle, buf):     // gfc 0x1391ef60
    ImmediatesEncoder.Encode(  bundle, msg, buf.ptr, buf.len)   // @7..86   (0x1eb5bd20)
    VectorScalarEncoder.Encode(bundle, msg, buf.ptr, buf.len)   // @87..110 (0x1ecd1e00)
    ScsScalarMiscEncoder.Encode(bundle,msg, buf.ptr, buf.len)   // @111..137 op@127 (0x1eb914a0)
    ScalarAlu1Encoder.Encode(  bundle, msg, buf.ptr, buf.len)   // @138..164 op@154 (0x1eb7cd00)
    ScalarAlu0Encoder.Encode(  bundle, msg, buf.ptr, buf.len)   // @165..191 op@181 (0x1eb693c0)
    StreamEncoder.Encode(      bundle, msg, buf.ptr, buf.len)   // oneof: op@181/154, payload@99 (0x1eb9b4c0)
    DmaEncoder.Encode(         bundle, msg, buf.ptr, buf.len)   // oneof: op@181/154, payload@87 (0x1eb5a3a0)
    // buf is zero-initialized by the EncodeBundle wrapper; no check-byte epilogue written.

GOTCHA — bundle 没有校验尾标;必须将其清零。 EncodeBundle wrapper 会在分派前分配并 memset 这 32 字节缓冲区,因为未激活的 slot 不写入任何内容,而全零 bundle 是规范的“所有 slot 均未激活”NOP。没有要追加的尾随 0x55 validity byte(TensorCore bundle 有,SC bundle 没有)。跳过 zero-init 会在未写入 slot 中留下旧 bit,decoder 会把它们解释为 live op。

Bundle 大小访问器

EncoderBase<… SparseCoreScsCodecBase …>::BundleSizeBytes(gfc 0x1e835260)不返回字面量;它分派 codec-metadata vtable:return (*(codec_metadata->vtable[+0x30]))(codec_metadata),即 vtable slot 6;对于 per-gen CodecMetadata 中的 sequencer type 3,它返回 32。同一间接调用对 TAC(seq 4)和 TEC(seq 5)返回 64。


27 位标量 Slot 模板

布局

三个标量 slot 共享一个内部模板;只有 slot base 不同。相对 slot 的 offset 已按 ScalarAlu0 编码器(0x1eb693c0)逐字节确认,其 BitCopy 调用写入 @165/5(x0)、@170/6(ScalarY)、@176/5(x1)、@181/6(opcode),以及位于 @187/3@190/1@187/4@191/1 的 predication header:

text
27-bit scalar slot (slot-relative; absolute = slot_base + offset)
  +0   w5   operand x0          scalar-register selector
  +5   w6   ScalarY             scalar-register-or-immediate selector
  +11  w5   operand x1          scalar-register selector
  +16  w6   OPCODE              6-bit primary opcode (≤ 64 ops)
  +22  w3   normal_predication  SparsecoreNormalPredication
  +22  w4   rotate_predication  overlaps normal when is_rotate (16-entry ring)
  +25  w1   predication_inversion
  +26  w1   is_rotate_predication
```text

因此绝对 opcode bit 就是 `base + 16`:`ScsScalarMisc` opcode `@127`(= 111+16)、`ScalarAlu1` `@154`(= 138+16)、`ScalarAlu0` `@181`(= 165+16)。三个 slot 以 27 bit 间隔直接叠放在 24 位 VectorScalar 桥之上。

| Slot | Base | x0 `@+0/5` | ScalarY `@+5/6` | x1 `@+11/5` | OPCODE `@+16/6` | pred header `@+22` |
|---|---:|---:|---:|---:|---:|---|
| `ScsScalarMisc` | 111 | 111 | 116 | 122 | **127** | 133/136/137 |
| `ScalarAlu1` | 138 | 138 | 143 | 149 | **154** | 160/163/164 |
| `ScalarAlu0` | 165 | 165 | 170 | 176 | **181** | 187/190/191 |

> **NOTE — predication header 是 3 位/4 位重叠,而不是两个字段。** `normal_predication`(3 位)和 `rotate_predication`(4 位)共享同一个起始 bit `@+22`;1 位 `is_rotate_predication` `@+26` 选择采用哪种解释。启用 rotating predication 时,4 位字段索引 16-entry rotating-predicate ring(6acc60406 增加 `SetRotatingPredicateRegister` 和 `BranchRelativeRotatingPreg` 来驱动它)。重新实现者不能分配 3+4 个不同 bit;它是具有两种含义的 4 个 bit。
>
> **GOTCHA — 两个 5 位 operand 字段是 operand selector,不是 result selector。** opcode 与 predication-header 位置是 bit-exact 的;x0/x1 的精确 operand-vs-result 角色遵循 scalar-ALU operand 模型(`ScalarY @+5` 是 ScalarY 字段工作中记录的 operand-or-immediate selector)。将 x0/x1 视为 register-source selector;destination 编码并未在此独立 bit-trace。

---

## SCS 标量 Opcode 清单

### 两级 opcode 空间

每个 SC opcode 都可以从其 op-form 的 `Opcode::Matches()` predicate 中逐字节恢复;libtpu 为每代每个 opcode 的每个 slot 生成一个 C++ 类型(`SparseCore<Slot><OpName>Opcode`),每个类型都有一个 `Matches()`,它会 mask 解码后的 opcode 字段,并与该 op 自身 signature 比较。`cmp`/mask 立即数*就是* opcode。会出现三种 predicate 形态:

```c
// Form A — flat 6-bit (e.g. ScsScalarMisc IntegerAdd, 0x1ebabf00):
return ((word_0x10 >> 63 | word_0x18 << 1) & 0x3F) == 0x0A;     // opcode straddles word boundary

// Form B — composite: 6-bit base + 5-bit sub-class (e.g. AtomicTileAdd, 0x1ebabbe0):
//   base = (bit63 | low5<<1);  ^base==0 AND (word & 0xF800000000000) ^ sub_sig == 0
return ((bit63 + 2*low5) ^ 0x08 | (word_0x10 & 0xF800000000000) ^ 0x800000000000) == 0;  // base 8, sub 1

// Form C — mask-compare (the ALU lanes, e.g. ScalarAlu0 IntegerAdd, 0x1eb67660):
return (word_0x18 & 0x7E0000000000000) == 0x140000000000000;    // (0x140.. >> 53) = 0x0A

6 位 primary opcode 选择具体 op 或 op 类别。op 类别(Sync、SyncWatch、Atomic、control、register-read、config-set)携带 secondary sub-opcode 字段。primary 值 0..63 是规范标量集合;更宽的编码(0x280+0x4001+0x16xxxx)是位于专用 bit 字段中的类别 escape。

共享 ALU 命名空间(ScalarAlu0 / ScalarAlu1)

两个 ALU lane 共享一个 opcode 命名空间:IntegerAdd=0x0aBitwiseAnd=0x0eCompareIntegerEq=0x1e、FP-compare 块 0x2a..0x2f 在两者上相同;区别只在于(a)bundle bit 位置(@181 vs @154)和(b)少量 lane 专属 op。共享 op 的值跨代不变(vfc IntegerAdd0x0a,与 gfc 逐字节一致)。

OpcodeMnemonic类别Lane
0x0aIntegerAddinteger ALUAlu0+Alu1
0x0bIntegerAddWithOverflowCheckinteger ALU两者
0x0c/0x0dIntegerSubtractYX / …WithOverflowCheckinteger ALU两者
0x0e/0x0f/0x10BitwiseAnd / BitwiseOr / BitwiseXorbitwise两者
0x11/0x12FloatingPointAdd / FloatingPointSubtractYXFP ALUAlu1
0x13FloatingPointMultiplyFP ALUAlu0
0x14/0x15Multiply32BitIntegers / …UnsignedReturningHighHalfinteger mulAlu0
0x16DivideWithRemainderXYinteger divAlu0
0x17/0x18/0x19LogicalShiftLeft/Right, ArithmeticShiftRight XByYPlacesshift两者
0x1a/0x1bMaxOfTwoFloatingPointValues / MinOf…FP minmax两者
0x1c/0x1dMaxOfTwoUnsignedIntValues / MinOf…int minmax两者
0x1e0x27CompareInteger{Eq,Ne} + signed/unsigned Gt/Gte/Lt/Ltecompare两者
0x28CarryOutFromIntegerUnsignedcarry两者
0x29PredicateOrpredicate两者
0x2a0x2fCompareFloatingPoint{Eq,Neq,Gt,Gte,Lt,Lte}FP compare两者
0x30IsInfOrNanFP classify两者
0x31ArithmeticShiftLeftXByYPlacesCheckOverflowshift两者
0x3eLogicalShiftLeftOnesXByYPlacesshift(仅 GF)Alu0

lane 专属 primary ALU op(仅 Alu1):0x01 ScalarLoadSmemY0x02 ScalarLoadSmemXY0x03 ScalarStoreXToSmemY0x09 DescriptorBasedDma0x32 ScalarStoreXToSmemSumDestAndY0x33 AddCbreg0x34 TaskRequestClearIbuf0x35 WriteCbreg0x36 ReadCbreg0x37 TaskRequest0x3c ScalarStoreCircularBuffer0x3d ScalarLoadCircularBuffer。(AddCbreg=0x33 已确认:(word6 & 0xFC000000) == 0xCC0000000xCC000000 >> 26 = 0x33。)

四种 class-escape 编码

当 primary 值表示一个类别时,具体 op 位于更宽的 escape 字段中。bit 位置依 lane 而异(Alu0 在结构体中的位置高于 Alu1);与 slot 无关。

Escape字段宽度值范围示例
Control11 位0x00..0x1dHalt=0x00, BranchAbsolute=0x04, BranchRelative=0x05, CallAbsolute=0x06, CallRelative=0x07, ScalarFence=0x09, ConvertInt32ToFloat32=0x0b, Delay=0x03, BranchRelativeRotatingPreg=0x18 (GF)
Register-read17 位0x280..0x28dReadRegisterLccLow=0x280, …GtcLow=0x282, …SparseCoreId=0x286, …Tileid=0x289, …TaskBitmap=0x28a, …DmaCreditRegister=0x28d
Config-set16 位0x4001..0x4005SetTag=0x4001, SetIndirectFilterValue=0x4002, SetDmaCredit=0x4003, SetDmaThrottleSflagRange=0x4004, SetRotatingPredicateRegister=0x4005 (GF)
Divide-push11 位(Alu0)0x160001..0x160002DivideWithRemainderXYPushQuotient, …PushRemainder

已验证锚点:Halt (word15 & 0x7FF) == 0 → control 0x00;BranchAbsolute (word3 & 0x7FF000000000000) == 0x40000000000000x4000000000000>>48 = 0x04ReadRegisterLccLow (word3 & 0x7FFFC…) == 0xA000…0xA000…>>42 = 0x280

QUIRK — register read 和 config set 并不是 6 位字段中的 opcode 0x28x/0x400x 它们不可能是,因为 primary opcode 只有 6 位。0x280+ / 0x4001+ 值位于更宽的 escape 字段(17 位 / 16 位)中,并在 primary opcode 标记 register-read / config-set 类别时覆盖该 slot。重新实现者看到 “ReadRegisterGtcLow = 0x282” 时,必须把 0x282 放入 17 位字段,而不是 6 位 opcode 字段。

ScsScalarMisc:atomic + sync slot

ScsScalarMisc(base 111,opcode @127)是 sync/atomic 引擎。它没有 FP,也没有 branch;它保存用于协调 SC tile 彼此之间以及与 TensorCore 之间关系的 sync-flag 族,以及一个从 ALU lane 复制而来的 integer-ALU 子集。opcode 空间高度复合:6 位 base 表示类别,5 位 sub-opcode(sync/atomic mode,位于 struct-relative bit 47;或 extended-ALU 类别,位于 bit 58)选择 op。

Base类别Sub-opcode 字段成员
0x00extended-ALUsub @bit58CoreInterrupt(0), CountLeadingZeros(14), MoveY(13)
0x01Sync compare-and-setmode @bit47SyncDone/Equal/NotEqual/Greater/GreaterOrEqual/Less/NotDone/…/…OrDone(12 种 mode)
0x02SyncWatchmode @bit47SyncWatch{Done,Equal,…,LessOrDone}(12 种 mode)
0x03SyncWatch escapesub @bit58SyncWatchWait(0), SyncWatchWaitSelect(1)
0x04SyncWatch escapesub @bit58SyncWatchEnd(0), SyncWatchEndSelect(1)
0x05set-syncmode @bit47SetSyncFlag(0), SetSyncDone(1), AddSyncFlag(2)
0x06read-syncsub @bit58ReadSyncFlag(0), ReadSyncDone(1), ReadSyncPublicAccess(2)
0x07barriermode @bit47SyncBarrier(0), SetPOrTState(4)
0x08Atomicop×set-done @bit47AtomicTile{Write,Add}[SetDone][Inverted], AtomicRemote{Write,Add}…(12 种形式)

ScsScalarMisc 还携带从 ALU 集合镜像而来的 flat 6-bit op:IntegerAdd=0x0aBitwiseAnd=0x0e、整数 compare 块 0x1e..0x27,以及 ReadSyncStateValue=0x2aReadSyncStateDone=0x2bSetTracemark=0x2dTrace=0x2eSetSyncFlagPublicAccess=0x2fSmemFetchAndAdd=0x38。(AtomicTileAdd 已确认为 base 8 / sub 1;IntegerAdd 已确认 ==0x0a。)

NOTE — 6acc60406 简化了 sync 模型。 Ghostlite 的 ScalarMisc 携带 100 个 op,包括双通道 sync 族(Set{Both,Other}Sync*Add{Both,Other}SyncFlag)和 Yieldable* sync op;6acc60406 移除了这两个族(降至 82 个 op),并增加单个 SetPOrTState。符号数量确认:glc 带有 272 个 Yieldable* 和 126 个 Set/Add{Both,Other}* SparseCore 符号,而 gfc 中为SetPOrTState 只出现在 gfc 中。解释是非 yield 型 tile scheduler:更少的 sync primitive,确定性延迟。面向 6acc60406 的重新实现者不得发出 Yieldable**Both*/*Other* sync op,因为 gfc 中没有它们的 encoder。

分代差异

共享 op 的标量 ISA 跨代不变;差异很小,集中在 halt/yield 和 rotating-predicate ring。

方面ViperfishGhostlite6acc60406
Bundle / slot 布局32 B,base 111/138/165相同相同
Primary opcode 宽度6 位6 位6 位
ScalarAlu op 数量(并集)969595
ScsScalarMisc op 数量10010082
Halt/yield opHaltYield, HaltYieldConditional, ReadRegisterYieldRequest, ScalarFenceScmf同族存在(移除)
Rotating-preg opBranchRelativeRotatingPreg, LogicalShiftLeftOnesXByYPlaces, SetRotatingPredicateRegister, MoveCbreg, ScalarStoreXToSmemSumDestAndY
IntegerAdd0x0a0x0a0x0a

SCS 如何向 TAC / TEC 发出工作

按 attribute 启动的模型

SCS 不在每条指令中嵌入数值型 engine selector。engine 分配是外层函数上的字符串 attribute,由 region→sequencer outliner 产生,并在 lowering 时读回。流程如下:

text
sc_tpu.tile_task region                         (the per-tile compute body)
   │  TileTaskOutliningPass::runOnOperation       0x13606220
   │    per-op callback                            0x136066e0

func.func(  live-in memrefs )  sc.sequencer = "execute"   ← the TEC body
   ▲                                  qmemcpy(…, "execute", 7) in the outliner callback 0x136066e0
   │  LaunchTileTaskOp::create        0x145dd0e0
enclosing func  sc.sequencer = "scs"                       ← the SCS control program

   ▼  read back at lowering
LowerSequencerFunctionsPass::runOnOperation     0x13532120
   │   ScDialect::HasCoreSequencerTypeAttribute   0x14599ec0  (reads "sc.sequencer", 12; "scs" path)
   │   ScDialect::HasExecuteSequencerTypeAttribute 0x1459a020 ("execute" path)

per-engine codec selected by TpuSequencerType {3=SCS, 4=TAC, 5=TEC}
```text

outliner(`TileTaskOutliningPass`,`0x13606220`)遍历每个 `sc_tpu.tile_task`,收集 region 中作为 outlined func 参数的 live-in `mlir::Value`(`getUsedValuesDefinedAbove`),根据 live-in memref shape 构建 `FunctionType`,把 region clone 到新的 `func.func`(`Region::cloneInto`),用 `cf.BranchOp` 连接入口,用 `LaunchTileTaskOp` 替换 `tile_task`,擦除原始 op,并给新函数标记 `sc.sequencer="execute"`。外层函数,也就是发出 launch 的 SCS 控制程序,携带 `sc.sequencer="scs"`。下游 `LowerSequencerFunctionsPass` 通过 `ScDialect` predicate 读回该字符串,并按 codec-template `TpuSequencerType`({3,4,5})选择 SCS/TAC/TEC encoder。

> **QUIRK — 二进制中唯一的 `sc.sequencer` 字符串值是 `"scs"` 和 `"execute"`。** 两个字符串字面量都存在(`"execute"` 出现在 5 处,`"scs"` 出现在 1 处;由 outliner callback 中的 `qmemcpy(…, "execute", 7)` 和其他位置的 `"scs", 3` 字面量验证)。二进制中没有任何 **`"access"` 字符串**;attribute 模型只表达 SCS↔TEC 分区,而 TileTaskOp 携带 `ParentFuncHasCoreSequencerTypeAttribute` trait,不携带 per-engine 字符串。Viperfish/Ghostlite 上 TAC 的存在是 codec-namespace 事实(`vxc.vfc`/`gxc.glc` 提供 `SparseCoreTacCodecBase`),不是 outliner-string 事实;在 6acc60406 上,`gfc` namespace 中没有任何 `SparseCoreTac*` 符号,因此 tile-fetch 折叠进 `"execute"`(TEC)函数。未观察到任何 pass 为 TAC 标记不同的 `"access"` attribute。

### SCS 用于 issue 的控制 / sync primitive

launch 与 handoff 的 SCS 侧机制由上面的标量清单构成:

- **地址设置** — integer ALU(`IntegerAdd`、`Multiply32BitIntegers`、shift/compare 块)计算 `table_base + index × stride`;结果通过 `ScalarStoreXToSmemY` / circular-buffer store 以及 CBREG op(`AddCbreg`、`WriteCbreg`、`ReadCbreg`、`MoveCbreg`)暂存在 SMEM 中。参见 [CBREG Circular-Buffer Register](cbreg.md)。
- **寄存器门控的前进** — `ReadRegisterDmaCreditRegister=0x28d`、`ReadRegisterFenceStatus=0x28b`、`ReadRegisterGtcLow/High=0x282/0x283`、`ReadRegisterTileid=0x289`、`ReadRegisterSparseCoreId=0x286` 读取节流 issue 的芯片寄存器;`SetDmaCredit=0x4003` 和 `SetDmaThrottleSflagRange=0x4004` 写入 DMA-credit/throttle 状态。
- **launch** — `TaskRequest=0x37` / `TaskRequestClearIbuf=0x34` 请求 tile task;lower 后的 `LaunchTileTaskOp` 携带 outlined TEC func。`BranchRelativeRotatingPreg`(GF)驱动内部循环 tile-fetch 分派,这在以前需要 TAC 的独立 sequencer。
- **握手** — `ScsScalarMisc` 发出 `SetSyncFlag`/`AddSyncFlag`(base 0x05)以及 `Sync*` compare-and-set 族(base 0x01),使 TEC 和 TensorCore 通过共享 sync-flag 池等待 / 发出完成信号;`Atomic*` 族(base 0x08)执行 tile/remote atomic write/add,并可选 set-done。
- **fence** — `ScalarFence=0x09`、`ScalarFenceStreamHbm=0x1c`、`ScalarFenceStreamSpmem=0x1d` 在 launch 返回前对未完成的 stream/DMA traffic 排序。

> **NOTE — SCS 发出 gather 的*控制*,而不是 gather 本身。** 实际的间接 HBM traffic 是 Stream/DMA op(SCS 标量 lane 的一种 `oneof` 形式,opcode `@181/154`),在 VF/GL 上由 TAC stream engine 消费,在 6acc60406 上由 TEC stream slot 消费。SCS 计算 index/address 并发出 descriptor;字节移动发生在 TAC/TEC 上。支撑 SparseCore 的 `STREAM_OPCODE_SCATTER_FLOAT_ADD` atomic-into-HBM primitive 就是这些形式之一。参见 [Stream Gather/Scatter](stream-gather-scatter.md)。

---

## 函数映射

| Symbol | Address | 角色 |
|---|---|---|
| `SparseCoreScsCodecBase::Encode` (gfc) | `0x1391ef60` | bundle 分派器;对每个 slot 编码器使用 shared-Span 调用 |
| `BitCopy` | `0x1fa0a900` | 小端 bit packer(`dst, dst_bitoff, src, src_bitoff, nbits`) |
| `SparseCoreScalarImmediatesEncoder::Encode` | `0x1eb5bd20` | 4 × 20 位立即数 `@7/27/47/67` |
| `SparseCoreVectorScalarEncoder::Encode` | `0x1ecd1e00` | scalar→vector 桥 `@87..110` |
| `SparseCoreScsScalarMiscEncoder::Encode` | `0x1eb914a0` | misc/sync/atomic slot,opcode `@127` |
| `SparseCoreScalarAlu1Encoder::Encode` | `0x1eb7cd00` | 标量 lane 1,opcode `@154` |
| `SparseCoreScalarAlu0Encoder::Encode` | `0x1eb693c0` | 标量 lane 0,opcode `@181`;模板 bit-exact |
| `SparseCoreStreamEncoder::Encode` | `0x1eb9b4c0` | Stream oneof-of-lane,opcode `@181/154`,payload `@99` |
| `SparseCoreDmaEncoder::Encode` | `0x1eb5a3a0` | Dma oneof-of-lane,opcode `@181/154`,payload `@87` |
| `EncoderBase<…gfc Scs…>::BundleSizeBytes` | `0x1e835260` | 分派 codec-metadata `vtable[+0x30]` → 32 |
| `SparseCoreScalarMiscIntegerAddOpcode::Matches` | `0x1ebabf00` | flat-6 predicate,`==0x0a`(form A) |
| `SparseCoreScalarMiscAtomicTileAddOpcode::Matches` | `0x1ebabbe0` | composite predicate,base 8 / sub 1(form B) |
| `SparseCoreScalarAlu0IntegerAddOpcode::Matches` | `0x1eb67660` | mask-compare,`(w&0x7E0…)==0x140…` → 0x0a(form C) |
| `SparseCoreScalarAlu0HaltOpcode::Matches` | `0x1eb67500` | control escape,`(w15&0x7FF)==0` → 0x00 |
| `SparseCoreScalarAlu0BranchAbsoluteOpcode::Matches` | `0x1eb67d40` | control escape → 0x04 |
| `SparseCoreScalarAlu0ReadRegisterLccLowOpcode::Matches` | `0x1eb67560` | 17 位 escape → 0x280 |
| `SparseCoreScalarAlu1AddCbregOpcode::Matches` | `0x1eb7b5a0` | `(w6&0xFC000000)==0xCC000000` → 0x33 |
| `TileTaskOutliningPass::runOnOperation` | `0x13606220` | region→sequencer outliner driver |
| `LowerSequencerFunctionsPass::runOnOperation` | `0x13532120` | 读取 `sc.sequencer`,降低 per-engine body |
| `ScDialect::HasCoreSequencerTypeAttribute` | `0x14599ec0` | predicate:`sc.sequencer == "scs"` |

跨代锚点:vfc SCS `ScalarAlu0` `0x1ee82ce0`(op `@181`)、`ScsScalarMisc` `0x1eeac160`(op `@127`);glc SCS `ScalarAlu0` `0x1e9d2140`(op `@181`)都与 gfc 逐字节一致。`Encode` 分派器和 slot base 在 VF/GL/GF 之间不变。

---

## 注意事项

- **Misc slot 中没有 FP,也没有 branch。** `ScsScalarMisc` 只包含 sync/atomic + integer-ALU;FP arithmetic、FP compare、branch、call 和 SMEM load/store 位于 ALU lane。scheduler 不得把 sync op 放入 ALU lane,也不得把 FP op 放入 Misc。
- **Lane 不对称。** `ScalarAlu1` 持有 SMEM load/store、CBREG 和 task-request op(`ScalarLoadSmemY`、`AddCbreg`/`WriteCbreg`/`ReadCbreg`、`TaskRequest`、`DescriptorBasedDma`,每个都只存在于 Alu1 命名空间中,Alu0=0);`ScalarAlu0` 持有 branch/call 和 divide-push op(`BranchAbsolute`、`CallAbsolute`、`DivideWithRemainderXYPushQuotient`,仅 Alu0)。许多 op(`IntegerAdd`、`Halt`、`ConvertInt32ToFloat32`)同时出现在两个 lane 中。尽管共享 opcode 命名空间,这两个 lane 仍不可互换。
- **6acc60406 的非 yield scheduler。** 被移除的 `Yieldable*` 和双通道 sync 族意味着 6acc60406 SCS 程序无法表达 cooperative yield-on-sync;它改为依赖 rotating-predicate ring 和确定性延迟。
- **未映射区域。** 7 位 bundle 前缀(`@0..6`)和 `192..255` padding 不由任何 slot 编码器写入;codec 是否在 epilogue 中设置 version/valid nibble 尚未解码(bundle 没有 `0x55` 尾标)。每个复合 `ScsScalarMisc` sub-opcode 字段(struct-relative bit 47/58 处的 sync/atomic mode)的绝对 bundle bit 是作为 within-struct offset 恢复的,但并未对所有 50 个复合 Misc op 固定到其绝对 bundle bit。

---

## 相关组件

| 名称 | 关系 |
|---|---|
| `SparseCoreScsCodecBase::Encode` (`0x1391ef60`) | 本页记录的 SCS bundle encoder |
| `TileTaskOutliningPass` (`0x13606220`) | 标记 `sc.sequencer`,分配 SCS parent 与 TEC body |
| `LowerSequencerFunctionsPass` (`0x13532120`) | 读回 `sc.sequencer` 并选择 per-engine codec |
| `getSequencerType` (`0x13507760`) | 将字符串 attribute 映射到 `TpuSequencerType` 的 accessor |

## 交叉引用

- [SparseCore Overview](overview.md) — 三类引擎、per-gen 存在性,以及 `TpuSequencerType` codec-template enum。
- [SparseCore Hardware Architecture](architecture.md) — SCS 面向的 geometry,以及 `SparseCoreTarget`/`TpuCoreParts` sequencer indexing(C++ `{3,4,5}` enum,并协调 proto 的 off-by-one)。
- [TAC Engine](tac-engine.md) — 复用 SCS 低区域 bundle 布局的 tile-fetch DMA issuer(仅 VF/GL)。
- [TEC (Vector) Engine](tec-engine.md) — SCS 程序通过 `LaunchTileTaskOp` 启动的宽向量引擎。
- [Scalar Opcode Enum](scalar-opcode-enum.md) — 完整 SCS / TAC scalar ALU 和 scalar-misc opcode 清单。
- [Bundle Slot-Base Map](bundle-slot-base-map.md) — SCS / TAC / TEC 的 per-engine 绝对 slot-bit 分区。
- [Region → Sequencer Outliner](region-to-sequencer-outliner.md) — 为每个 region 分配引擎的 `TileTaskOutliningPass`。
- [getSequencerType](getsequencertype.md) — SCS/TAC/TEC engine-selection accessor。
- [CBREG Circular-Buffer Register](cbreg.md) — SCS 用 `AddCbreg`/`ReadCbreg`/`WriteCbreg`/`MoveCbreg` 管理的环形缓冲寄存器。
- [M-Register Predicate Word](m-register-predicate.md) — 覆盖 27 位标量 slot 的 predication header。
- [Stream Gather/Scatter](stream-gather-scatter.md) — Stream/DMA oneof-of-lane descriptor 和 `STREAM_OPCODE_*` 集合。
- [SC Backend Pipeline](sc-backend-pipeline.md) — outliner 和 sequencer-lowering pass 在 SC-MLO pipeline 中的位置。
- [SC EmitX Dispatcher](sc-emitx-dispatcher.md) — 驱动 per-engine emission 的 seq3/seq4/seq5 → EmitX jump table。
- **Binary:** `extracted/libtpu-0.0.40-cp314-cp314-manylinux_2_31_x86_64/libtpu/libtpu.so`(build-id `89edbbe81c5b328a958fe628a9f2207d`)
- **Index entry:** Part IX — SparseCore & BarnaCore / SparseCore engines — [返回索引](../index.md)