SCS(标量)引擎
本页中的每个地址、偏移、opcode 值和 bundle 位位置,均从
libtpu-0.0.40-cp314wheel(build-id89edbbe81c5b328a958fe628a9f2207d)中的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:ScsScalarMisc、ScalarAlu1、ScalarAlu0,它们的 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),然后是位于 base111/138/165的三个 27 位标量 slotMisc/Alu1/Alu0。bit0..6和192..255不由任何 slot 编码器写入。此布局在 VF/GL/GF 上固定。 - 27 位标量 slot 模板。 相对 slot:x0
@+0/5、ScalarY@+5/6、x1@+11/5、OPCODE@+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 enum | TpuSequencerType = 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 packer | BitCopy(dst, dst_bitoff, src, src_bitoff, nbits) 0x1fa0a900(小端) |
| 标量 slot | ScsScalarMisc(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_taskregion 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。
| Marketing | Codename | Family ns | SCS | bundle | 备注 |
|---|---|---|---|---|---|
| TPU v5p | Viperfish | vxc.vfc | Y | 32 B | 首个三引擎拆分 SCS+TAC+TEC |
| TPU v6e | Ghostlite | gxc.glc | Y | 32 B | 完整 SCS+TAC+TEC;TEC 加宽 |
| TPU7x | 6acc60406 | gxc.gfc | Y | 32 B | 无 TAC;SCS 增加 rotating-preg op |
NOTE — 两个 enum 空间都会给 SparseCore sequencer 编号,但它们相差一(proto-vs-C++)。 C++
tpu::TpuSequencerTypeenum 编号为 SCS=3 / TAC=4 / TEC=5,这由 demangledEncoderBase<… 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 enumTpuSequencerTypeProto,它保留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 上逐字节一致。
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 没有校验尾标;必须将其清零。
EncodeBundlewrapper 会在分派前分配并memset这 32 字节缓冲区,因为未激活的 slot 不写入任何内容,而全零 bundle 是规范的“所有 slot 均未激活”NOP。没有要追加的尾随0x55validity 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:
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) = 0x0A6 位 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=0x0a、BitwiseAnd=0x0e、CompareIntegerEq=0x1e、FP-compare 块 0x2a..0x2f 在两者上相同;区别只在于(a)bundle bit 位置(@181 vs @154)和(b)少量 lane 专属 op。共享 op 的值跨代不变(vfc IntegerAdd 为 0x0a,与 gfc 逐字节一致)。
| Opcode | Mnemonic | 类别 | Lane |
|---|---|---|---|
0x0a | IntegerAdd | integer ALU | Alu0+Alu1 |
0x0b | IntegerAddWithOverflowCheck | integer ALU | 两者 |
0x0c/0x0d | IntegerSubtractYX / …WithOverflowCheck | integer ALU | 两者 |
0x0e/0x0f/0x10 | BitwiseAnd / BitwiseOr / BitwiseXor | bitwise | 两者 |
0x11/0x12 | FloatingPointAdd / FloatingPointSubtractYX | FP ALU | Alu1 |
0x13 | FloatingPointMultiply | FP ALU | Alu0 |
0x14/0x15 | Multiply32BitIntegers / …UnsignedReturningHighHalf | integer mul | Alu0 |
0x16 | DivideWithRemainderXY | integer div | Alu0 |
0x17/0x18/0x19 | LogicalShiftLeft/Right, ArithmeticShiftRight XByYPlaces | shift | 两者 |
0x1a/0x1b | MaxOfTwoFloatingPointValues / MinOf… | FP minmax | 两者 |
0x1c/0x1d | MaxOfTwoUnsignedIntValues / MinOf… | int minmax | 两者 |
0x1e–0x27 | CompareInteger{Eq,Ne} + signed/unsigned Gt/Gte/Lt/Lte | compare | 两者 |
0x28 | CarryOutFromIntegerUnsigned | carry | 两者 |
0x29 | PredicateOr | predicate | 两者 |
0x2a–0x2f | CompareFloatingPoint{Eq,Neq,Gt,Gte,Lt,Lte} | FP compare | 两者 |
0x30 | IsInfOrNan | FP classify | 两者 |
0x31 | ArithmeticShiftLeftXByYPlacesCheckOverflow | shift | 两者 |
0x3e | LogicalShiftLeftOnesXByYPlaces | shift(仅 GF) | Alu0 |
lane 专属 primary ALU op(仅 Alu1):0x01 ScalarLoadSmemY、0x02 ScalarLoadSmemXY、0x03 ScalarStoreXToSmemY、0x09 DescriptorBasedDma、0x32 ScalarStoreXToSmemSumDestAndY、0x33 AddCbreg、0x34 TaskRequestClearIbuf、0x35 WriteCbreg、0x36 ReadCbreg、0x37 TaskRequest、0x3c ScalarStoreCircularBuffer、0x3d ScalarLoadCircularBuffer。(AddCbreg=0x33 已确认:(word6 & 0xFC000000) == 0xCC000000,0xCC000000 >> 26 = 0x33。)
四种 class-escape 编码
当 primary 值表示一个类别时,具体 op 位于更宽的 escape 字段中。bit 位置依 lane 而异(Alu0 在结构体中的位置高于 Alu1);值与 slot 无关。
| Escape | 字段宽度 | 值范围 | 示例 |
|---|---|---|---|
| Control | 11 位 | 0x00..0x1d | Halt=0x00, BranchAbsolute=0x04, BranchRelative=0x05, CallAbsolute=0x06, CallRelative=0x07, ScalarFence=0x09, ConvertInt32ToFloat32=0x0b, Delay=0x03, BranchRelativeRotatingPreg=0x18 (GF) |
| Register-read | 17 位 | 0x280..0x28d | ReadRegisterLccLow=0x280, …GtcLow=0x282, …SparseCoreId=0x286, …Tileid=0x289, …TaskBitmap=0x28a, …DmaCreditRegister=0x28d |
| Config-set | 16 位 | 0x4001..0x4005 | SetTag=0x4001, SetIndirectFilterValue=0x4002, SetDmaCredit=0x4003, SetDmaThrottleSflagRange=0x4004, SetRotatingPredicateRegister=0x4005 (GF) |
| Divide-push | 11 位(Alu0) | 0x160001..0x160002 | DivideWithRemainderXYPushQuotient, …PushRemainder |
已验证锚点:Halt (word15 & 0x7FF) == 0 → control 0x00;BranchAbsolute (word3 & 0x7FF000000000000) == 0x4000000000000 → 0x4000000000000>>48 = 0x04;ReadRegisterLccLow (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 字段 | 成员 |
|---|---|---|---|
0x00 | extended-ALU | sub @bit58 | CoreInterrupt(0), CountLeadingZeros(14), MoveY(13) |
0x01 | Sync compare-and-set | mode @bit47 | SyncDone/Equal/NotEqual/Greater/GreaterOrEqual/Less/NotDone/…/…OrDone(12 种 mode) |
0x02 | SyncWatch | mode @bit47 | SyncWatch{Done,Equal,…,LessOrDone}(12 种 mode) |
0x03 | SyncWatch escape | sub @bit58 | SyncWatchWait(0), SyncWatchWaitSelect(1) |
0x04 | SyncWatch escape | sub @bit58 | SyncWatchEnd(0), SyncWatchEndSelect(1) |
0x05 | set-sync | mode @bit47 | SetSyncFlag(0), SetSyncDone(1), AddSyncFlag(2) |
0x06 | read-sync | sub @bit58 | ReadSyncFlag(0), ReadSyncDone(1), ReadSyncPublicAccess(2) |
0x07 | barrier | mode @bit47 | SyncBarrier(0), SetPOrTState(4) |
0x08 | Atomic | op×set-done @bit47 | AtomicTile{Write,Add}[SetDone][Inverted], AtomicRemote{Write,Add}…(12 种形式) |
ScsScalarMisc 还携带从 ALU 集合镜像而来的 flat 6-bit op:IntegerAdd=0x0a、BitwiseAnd=0x0e、整数 compare 块 0x1e..0x27,以及 ReadSyncStateValue=0x2a、ReadSyncStateDone=0x2b、SetTracemark=0x2d、Trace=0x2e、SetSyncFlagPublicAccess=0x2f、SmemFetchAndAdd=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。
| 方面 | Viperfish | Ghostlite | 6acc60406 |
|---|---|---|---|
| Bundle / slot 布局 | 32 B,base 111/138/165 | 相同 | 相同 |
| Primary opcode 宽度 | 6 位 | 6 位 | 6 位 |
ScalarAlu op 数量(并集) | 96 | 95 | 95 |
ScsScalarMisc op 数量 | 100 | 100 | 82 |
| Halt/yield op | HaltYield, HaltYieldConditional, ReadRegisterYieldRequest, ScalarFenceScmf | 同族存在 | (移除) |
| Rotating-preg op | — | — | BranchRelativeRotatingPreg, LogicalShiftLeftOnesXByYPlaces, SetRotatingPredicateRegister, MoveCbreg, ScalarStoreXToSmemSumDestAndY |
IntegerAdd 值 | 0x0a | 0x0a | 0x0a |
SCS 如何向 TAC / TEC 发出工作
按 attribute 启动的模型
SCS 不在每条指令中嵌入数值型 engine selector。engine 分配是外层函数上的字符串 attribute,由 region→sequencer outliner 产生,并在 lowering 时读回。流程如下:
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)