Skip to content

SPU / 标量槽位

本页中的每个偏移、地址和 bit 位置都从 libtpu-0.0.40-cp314 wheel 中的 libtpu.so 按字节精确读取(BuildID md5 89edbbe81c5b328a958fe628a9f2207d)。其他版本会不同。

摘要

SPU(scalar-processing unit,在 V5+ ISA namespace 中为 ScalarAlu)是 TensorCore 的逐 core 控制 CPU lane。它拥有 32-entry scalar register file(SREGs),计算 loop bounds、memory addresses、strides 和 predicate values,运行 branch/call/halt control flow,并通过 V2S FIFO 连接 scalar 和 vector engine。在 VLIW bundle 中,SPU 表现为两个 scalar lanes — Jellyfish 和 Pufferfish 上为 scalar_0/scalar_1,Viperfish/Ghostlite/6acc60406 上为 ScalarAlu0/ScalarAlu1,SparseCore sequencer 上为 SparseCoreScalarAlu0/1。Lane 0 携带完整 op set,包括 branch/call;lane 1 是 ALU+halt 镜像。这是 ARM 风格的 predicated、statically-scheduled execution:没有 scoreboard,compiler 证明 bundle legality(见 Bundle Model)。

有两个 encoder family 产生 slot bytes。Jellyfish/Dragonfish (JXC) 是 direct-pack encoder:EncoderJf::EncodeScalarInstruction0x1e862060)通过 literal shl/and/or 算术把每个字段 OR 到 encoder struct byte +0x2D 处的 64-bit slot word 中,然后切出 41-byte bundlePufferfish/Viperfish/Ghostlite/6acc60406 (PXC/VXC/GXC) 将每个字段路由进通用 little-endian bit packer BitCopy(dst, dst_bit, src, src_bit, nbits)0x1fa0a900),由叶子 *ScalarAlu0Encoder::Encode method 对每个字段调用一次;该 method 的主体是在 proto opcode [instr+0x50] 上的 switch。本页中的字段位置从这些叶子 encoder 中的 BitCopy(buf, <bitoff>, …, <nbits>) call triples 读取,这就是每一行都以具体 bit offset 按字节锚定的原因。

本页以重新实现级别记录六个代际的 SPU:two-lane slot model 和 lane-0/lane-1 ownership split、逐代际 bit-field grid(opcode class、sub-opcode、predicate、X/Y/dst register operands)、统一的 6-bit ScalarYEncoding operand selector 及其 14 个 hardwired constants、scalar register file bound、immediate-slot geometry、scalar address-computation ops、V2S bridge,以及 JF→GF field-layout evolution。

对重新实现来说,契约是:

  • two-lane modelscalar_0/ScalarAlu0(完整 op set,branch/call 合法)vs scalar_1/ScalarAlu1(ALU+halt 镜像,无 branch/call),并在 encode time 强制 kMaxScalarSlotsPerBundle = 2
  • 逐代际 field grid:opcode-class + sub-opcode + predicate-bit + X/Y/dst register fields,每个都位于其 BitCopy 调用命名的 absolute bit offset;以及 JF 在 struct word +0x2D 中的 direct-pack 等价形式。
  • ScalarYEncoding 6-bit operand model0..0x1f = SREG,0x20..0x25 = immediate-slot reference,0x2e..0x3b = hardwired constant。
  • opcode dispatch:由 proto opcode 索引的叶子 switch/jump-table,该 proto opcode 位于 [instr+0x50];case 数量就是逐代际 scalar ISA 大小(JF 0..0x3E,PF ≤0x33,VF ≤0x4C / lane1 ≤0x52,VF-SCS ≤0x57,GL/GF ≤0x49)。
  • JF→GF 收缩:opcode-class 宽度 6→5→(4+6)→(4+6)→(2+6),predicate model 从 in-slot 5-bit field 到 in-slot 1-bit selector,再到 6acc60406 (GF) 上的 dedicated dual-predicate slot,以及 16-bit→20-bit immediate-slot 扩宽。
Lanes / bundle2(scalar_0/scalar_1,lane 0 = full + branch/call,lane 1 = ALU+halt mirror)
JF encoderEncoderJf::EncodeScalarInstruction(ScalarInstruction const&, int lane, Bundle const&) @ 0x1e862060
V4+ packerBitCopy(dst, dst_bit, src, src_bit, nbits) @ 0x1fa0a900 (_Z7BitCopyPviPKvii)
VF lane-0 encodervxc::isa::TensorCoreScalarAlu0Encoder::Encode @ 0x1eecb900
Scalar registers32 个 SREG,5-bit field;由 EncodingToScalarRegister @ 0x1e871e40 约束(idx > 0x1F → error)
Y operand6-bit ScalarYEncoding:SREG / imm-slot / hardwired constant
Immediate slots每 bundle 6 个;JF 上 16-bit,V5+ 上 20-bitBitCopy … nbits=20
Opcode sourceproto field at [instr+0x50],由逐代际叶子 switch dispatch
Confidence除非某行另有说明,否则为 CONFIRMED(byte-anchored)

Two-Lane Slot Model

每个代际每个 bundle 最多发出两个 scalar ops。两个 lanes 对称:lane 0 拥有完整 opcode space(arithmetic、compare、convert、memory、control flow、FIFO bridge),而 lane 1 是受限镜像,可以运行 ALU/halt 子集,但不能运行 branches 或 calls。这种不对称由 encoder 强制,而不只是约定。

在 Jellyfish 上,EncodeScalarInstruction 接受显式 lane 参数 a3,并以硬检查开头:

c
// EncoderJf::EncodeScalarInstruction(instr, lane, bundle)  @ 0x1e862060 (lines 216-223)
if (lane > 1)                                          // kMaxScalarSlotsPerBundle == 2
    CHECK_FAIL("slot < kMaxScalarSlotsPerBundle");     // encoder_jf.cc:1261
// branch (opcode 0xA) and call (0xC..0xF) reject lane != 0:
case 0xA: if (lane != 0) CHECK_FAIL("slot == 0");      // BranchRelative  (line 433)
case 0xE: if (lane != 0) CHECK_FAIL("slot == 0");      // Call            (line 476)
// scalar load/store/DMA-issue reject lane != 1:
case 4,5: if (lane != 1) CHECK_FAIL("slot == 1");      // ScalarLoad      (line 337)
case 6:   if (lane != 1) CHECK_FAIL("slot == 1");      // ScalarStore     (line 369)
```text

因此在 Jellyfish 上,*control-flow* ops 绑定到 lane 0,而 *memory* ops 绑定到 lane 1 — 这是两个 lane 按功能进行的有意静态划分,是 V5+ “branch/call 只在 lane 0 合法”规则的 JF 类比。V5+ encoder 以结构方式表达同样的拆分:存在独立的 `TensorCoreScalarAlu0Encoder` 和 `TensorCoreScalarAlu1Encoder` 叶子 method,且只有 lane-0 jump table 包含 `BranchAbsolute`/`CallSreg` case(lane 1 的 table case 数更大,但只有 halt+ALU cases 合法 — 见 [Sequencer Slot](slot-sequencer.md))。

> **QUIRK — lane 1 不是“scalar slot 2”,而是受限 twin。** 如果重新实现把两个 scalar lanes 当作可互换,就会把 branch 放进 lane 1,并产生硬件 sequencer 无法执行的 bundle。legality predicate 必须拒绝 lane 1 中的 control flow 和 FIFO-return ops;JXC encoder 用 `CHECK_FAIL("slot == 0")` 实现这一点,VXC/GXC encoder 则通过根本不发射 lane-1 control-flow case 实现。

两个 lanes 相邻堆叠在 bundle 的**高端**(control state 位于 sequencer 首先读取的位置),lane 0 在 lane 1 之上。在 Viperfish 上,lane 0 大致占据 512-bit bundle 的 bits 477503,lane 1 大致占据 bits 454476 — lane-1 字段恰好是 lane-0 字段下移 27 bits(lane-0 class @499 → lane-1 class @472;sub @493 → @466;X @488 → @461)。

---

## `BitCopy` Field Packer

所有 V4+ scalar fields 都由一个函数放置:

```c
// _Z7BitCopyPviPKvii  @ 0x1fa0a900
void BitCopy(void*       dst,        // bundle byte buffer (the Span<uint8_t>)
             int         dst_bitoff, // absolute bit offset into the bundle
             const void* src,        // pointer to the field value word
             int         src_bitoff, // bit offset into the source word (always 0 for scalar fields)
             int         nbits);     // field width in bits

它是通用的、跨 byte 的 little-endian unaligned bit-field copy(if (!nbits) return; word = dst_bitoff / 8; …),对 nbits >= 24 有 AVX2-vectorized fast path,但这与 ≤6-bit scalar fields 无关。每个叶子 scalar encoder 都把 field value 加载到 stack slot,然后每个字段调用一次 BitCopy。代表性 prologue 如 Viperfish lane-0 header:

c
// vxc::isa::TensorCoreScalarAlu0Encoder::Encode(this, instr, span)  @ 0x1eecb900 (lines 26-30)
v22[0] = *(int*)(instr + 32);   BitCopy(span, 499, v22, 0, 4);   // opcode-class  @bit 499, 4b
v22[0] = *(uint8*)(instr + 24); BitCopy(span, 503, v22, 0, 1);   // predicate bit @bit 503, 1b
switch (*(uint32_t*)(instr + 80)) {                              // opcode = [instr+0x50]
    case 6:  BitCopy(span, 493, &zero, 0, 6);                    // sub-opcode    @bit 493, 6b
             BitCopy(span, 488, &Xreg, 0, 5); break;             // X register    @bit 488, 5b
    case 8:  …  BitCopy(span, 482, &Yenc, 0, 6);                 // Y encoding    @bit 482, 6b
             BitCopy(span, 477, &dst,  0, 5); break;             // dst register  @bit 477, 5b

}
```text

opcode-class 从 `[instr+0x20]` 读取(proto field 位于 offset 0x20,decompiler 中显示为 `instr+32`),predicate 从 `[instr+0x18]` 读取,dispatch opcode 从 `[instr+0x50]` 读取。下面引用的每个 bit offset 都是这些 `BitCopy` 调用之一的 literal first argument。

---

## 逐代际字段布局

五个代际共享一组逻辑字段 — opcode class、sub-opcode、predicate,以及 X/Y/dst register operands — 但由于 bundle width 和 predicate model 变化,每代把它们放在不同 bit。opcode 是 slot 顶部的一个小 **class** field,下面是 6-bit **sub-opcode**;class+sub 组合共同索引叶子 `switch`。下表是合并后的、按字节验证的 grid(所有 offset 都是 absolute bits,LSB-first):

| Gen (lane 0) | bundle | opcode class | sub-opcode | predicate | X reg | Y enc | dst reg | jump table | source |
|---|---:|---|---|---|---|---|---|---|---|
| **Jellyfish** | 41 B | opcode 6b @abs **311**(word `+0x2D` 中 `<<47`) | —(单个 6-bit opcode) | 5b @abs **317** (`<<53`) | 5b | 6b ScalarY | 5b | 0..0x3E | `0x1e862060` |
| **Pufferfish** | 51 B | opcode 5b @bit **403** | 6b @bit **397** | opcode value `0x1F`=NeverExecute | 6b @bit **386** | 6b ScalarY ||0x33 | `0x1ed16dc0` |
| **Viperfish** | 64 B | 4b @bit **499** | 6b @bit **493** | 1b @bit **503** | 5b @bit **488** | 6b @bit **482** | 5b @bit **477** |0x4C | `0x1eecb900` |
| **Ghostlite** | 64 B | 4b @bit **502** | 6b @bit **496** | 1b @bit **506** | 5b @bit **491** | 6b @bit **485** | 5b @bit **480** |0x49 | `0x1f219b40` |
| **6acc60406** | 64 B | 2b @bit **489** | 6b @bit **483** | (dual-pred slot) | 5b @bit **478** || 5b @bit **467** |0x49 | `0x1f87b420` |
| **VF SparseCore** | 32 B | 4b @bit **187** | 6b @bit **181** | 1b @bit **191** | 5b @bit **176** | 6b ||0x57 | `0x1ee82ce0` |

对表格最干净的解读是 **Ghostlite = Viperfish + 3** 关系:每个 GL 字段都恰好比对应 VF 字段高 3 bits(499502503506493496488491482485477480),这是 [Ghostlite Bundle](bundle-gl.md) 归因于 MXU/VALU opcode 各加宽 1 bit 的统一 `+3` 位移。随后 6acc60406 (GF) 完全*移除* in-slot predicate(`0x1f87b420` 中没有对 predicate bit 的 `BitCopy`),并将 class field 缩到 2 bits,把 slot header 下移约 13 bits,以便为专用 [`TensorCorePredicates`](slot-predicate.md) dual-predicate slot 腾出空间。

### Jellyfish — struct word `+0x2D` 处的 direct-pack

Jellyfish 不使用 `BitCopy`。`EncodeScalarInstruction` 在 encoder 的 `this+0x2D` 处维护 64-bit slot word,并根据 lane 参数分支,用 shift/mask 把字段 OR 进去:

```c
// EncoderJf::EncodeScalarInstruction  @ 0x1e862060 (lines 239-253)
pred = EncodePredication(instr) & 0x1F;
word = *(uint64*)(this + 0x2D);
if (lane == 1) {                                          // scalar_1
    word = (pred << 26)         | (word & 0xFFFF'FFFF'83FF'FFFF); // predicate @ word-bit 26
    word = ((opcode & 0x3F)<<20)| (word & 0xFFFF'FFFF'FC0F'FFFF); // opcode    @ word-bit 20
} else {                                                  // scalar_0 (sequencer lane)
    word = (pred << 53)         | (word & 0xFC1F'FFFF'FFFF'FFFF); // predicate @ word-bit 53
    word = ((opcode & 0x3F)<<47)| (word & 0xFFE0'7FFF'FFFF'FFFF); // opcode    @ word-bit 47
}
*(uint64*)(this + 0x2D) = word;
// operands then OR'd from ScalarBinaryOperands (X, Y, ScalarY at lane-dependent shifts)

由于 41-byte wire bundle 是 struct[0x0C..0x34],struct byte 0x2D 中 word-bit S 的字段落在 absolute bundle bit 0x2D*8 + S − 96:lane-0 opcode 0x2D*8 + 47 − 96 = 311,lane-1 opcode 0x2D*8 + 20 − 96 = 284。opcode 是单个 6-bit field(opcode & 0x3F),没有单独 class;switch (opcode) 覆盖 cases 0..0x3E,legal set 稀疏,并拒绝 opcode > 0x3E"Not implemented yet.",encoder_jf.cc:1439)。

lane==0(sequencer)分支将 opcode 左移 47(abs 311),predicate 左移 53(abs 317);lane==1 将 opcode 左移 20(abs 284)。lane-1 opcode placement 与 Jellyfish Bundle scalar-slot table 一致。

Pufferfish — 带 NeverExecute 重载的 5-bit opcode

Pufferfish 在 bit 403(51-byte bundle 的最后一个 byte)放置单个 5-bit opcode,在 397 放置 6-bit sub-opcode,在 386 放置 6-bit operand:

c
// pxc::isa::TensorCoreScalar0Encoder::Encode  @ 0x1ed16dc0 (lines 25-44)
v21[0] = *(int*)(instr + 32);  BitCopy(span, 403, v21, 0, 5);   // opcode @bit 403, 5b
switch (*(uint32_t*)(instr + 80)) {
    case 7: BitCopy(span, 397, v21, 0, 6);                       // sub-opcode @bit 397, 6b
            BitCopy(span, 386, v21, 0, 6); …                     // operand    @bit 386, 6b
}
```text

> **NOTE —** Pufferfish 的 scalar slot 中没有单独的 1-bit predicate field。5-bit opcode field 兼作 slot-skip marker:写入值 `0x1F`(`NeverExecute`)就是标记未使用 Pufferfish scalar slot 的方式,这与 [NOP encoding](nop-canonical.md) 在整个 bundle 中使用的约定相同。完整的 PXC immediate geometry(通过 `TensorCoreMiscEncoder` `ScalarY` 5-bit fields 路由)在这里没有按字节钉住 — 见 *Limits*

---

## `ScalarYEncoding` Operand Model

每个 scalar op 的 “Y” operand 都是一个 6-bit selector,由 `ProtoUtils::EncodingToScalarRegister`(`0x1e871e40`)和 `ScalarY` constants map 解码。取值空间是三路 union

| `ScalarYEncoding` value | 含义 |
|---|---|
| `0x00 .. 0x1f` (031) | SREG number — Y source register |
| `0x20 .. 0x25` (3237) | bundle immediate-slot reference imm0..imm5 |
| `0x2e .. 0x3b` (4659) | hardwired constant(见下表) |

`EncodingToScalarRegister` 是 register-decode 半边,其 bound *就是* register file 深度为 32 的证据:

```c
// ProtoUtils::EncodingToScalarRegister(ScalarYEncoding)  @ 0x1e871e40 (lines 13-30)
if (encoding > 0x1F) {                                  // > 31
    return Error("Input is not a valid register encoding. "
                 "Input must be in the range [%d, %d]", 0, 31);  // proto_utils.cc:917
}
out->reg = encoding;  out->status = OK;

hardwired-constant 半边让 SPU 能引用常见 scalar values,而不消耗六个 immediate slots 中的任何一个;它来自 rodata key/value pair,进入 ProtoUtils::GetConstants<ScalarYEncoding>0x1e870700):

EncRawValueEncRawValue
0x2e0x00000001int 10x350xc0000000−2.0f
0x2f0xffffffffint −10x360x3f0000000.5f
0x300x000000000 / 0.0f0x370xbf000000−0.5f
0x310x80000000−0.0f0x380x40490fdbπ
0x320x3f8000001.0f0x390xc0490fdb−π
0x330xbf800000−1.0f0x3a0x402df854e
0x340x400000002.0f0x3b0xc02df854−e

QUIRK — constant table 是 loop stride 很少消耗 immediate slot 的原因。 ±1 stride、0 比较阈值,以及 reciprocal-Newton seeds(±0.5、±2.0)主要由这 14 个值覆盖。0x2e..0x3b 中的 ScalarYEncoding 会完全在 slot 内解析 Y operand,让全部六个 20-bit immediate slots 留给真正任意的常量。把每个常量都路由到 immediate slot 的重新实现会虚假地耗尽 slot。


Scalar Register File

GenSREGsfield widthspill backingscalar-load latency
Jellyfish / Dragonfish325-bitSMEM(2 banks)2 cyc
Pufferfish325-bitSMEM(8 banks)4 cyc
Viperfish / Ghostlite / 6acc60406325-bitSMEM(8 banks)6 cyc

32-entry / 5-bit 数量在二进制中由三条独立证据绑定:EncodingToScalarRegister 拒绝 > 0x1F;每个逐代际 register operand BitCopy 都是 nbits=5(VF X@488、dst@477;GL X@491、dst@480;GF X@478、dst@467);调试标志 FLAGS_ForceTargetNumScalarRegs0x22542da8)只能把数量 clamp 到硬件 32 以下。SMEM 是 spill backing store — 没有 SMEM register window。当 LSRA-v2 无法让某个 SREG 在 region 间保持 live 时,它会向保留 spill region 发出 ScalarStore*ToSmem*,并在下一次使用处发出 ScalarLoadSmem*;spill region size 由 FLAGS_xla_jf_lsra_v2_reserved_smem0x223afaa8)设置。


Scalar Op Set

权威逐代际 op roster 就是叶子 encoder switch 本身:每个 case N 返回一个 Encode…<OpName> thunk,所以 case set 就是 ISA。Viperfish lane-0 switch0x1eecb900,cases 6–0x4C,77 ops)分组如下;Ghostlite 和 6acc60406 是此集合减去它们的差异。

text
Integer ALU    IntegerAdd, IntegerAddWithOverflowCheck, IntegerSubtractYX(+OverflowCheck),
               Multiply32BitIntegers, Multiply32BitUnsignedIntsReturningHighHalf,
               CarryOutFromIntegerUnsigned,
               DivideWithRemainderXY{,PushQuotient,PushRemainder}
Float ALU      FloatingPointMultiply, Max/MinOfTwoFloatingPointValues,
               Max/MinOfTwoUnsignedIntValues
Bitwise        BitwiseAnd, BitwiseOr, BitwiseXor
Shift          LogicalShiftLeftXByYPlaces, LogicalShiftRightXByYPlaces,
               ArithmeticShiftLeftXByYPlacesCheckOverflow, ArithmeticShiftRightXByYPlaces
Compare        CompareFloatingPoint{Eq,Neq,Gt,Gte,Lt,Lte}, CompareInteger{Eq,Ne},
               CompareSignedInteger{Gt,Gte,Lt,Lte}, CompareUnsignedInteger{Gt,Gte,Lt,Lte}
Convert        ConvertInt32ToFloat32, ConvertFloat32ToInt32, Ceiling, Floor,
               CountLeadingZeros, IsInfOrNan
Move           MoveY
Control        BranchAbsolute/Relative/Sreg, CallAbsolute/Relative/Sreg,
               HaltYield, HaltYieldConditional, Delay, ScalarFence, PredicateOr, SetTag
FIFO bridge    PopV2s, PopDrf, PopSfrf, PopSccf, PushSccf
Register reads ReadRegisterLccLow/High (loop counter), ReadRegisterGtcLow/High (time counter),
               ReadRegisterTag, ReadRegisterTcid, ReadRegisterTracemark, ReadRegisterYieldRequest
```text

Pufferfish 使用带 `Scalar` 前缀的助记符(`ScalarIntAdd`、`ScalarFloatMul`、`ScalarBranchAbsolute`、`ScalarPopV2s`、`ScalarLoadSmem`、`ScalarLoadSmemOffset`、`ScalarStoreSmemAbsolute`、`ScalarDma*` family、`ScalarSetRegister`、`ScalarReadRegisters` — 52 个 lane-0 cases,≤0x33)。它**没有** `HaltYield*`,也**没有** `ReadRegisterLcc*`(PF TensorCore 上没有硬件 loop counter)。opcode→mnemonic case set 从 encoder switch 按字节恢复(VF 内联于 `0x1eecb900`;PF 于 `0x1ed16dc0`;GL 在 `0x1f219b40` 有 64 个 distinct cases;GF 在 `0x1f87b420` 有 65 个)。

### Address-computation ops

SPU 计算所有 scalar addresses。encoding 跨两个 family 演化:

```text
JF / PF (JXC/PXC)   ScalarLoadSmem        SREG <- SMEM[abs imm]
                    ScalarLoadSmemOffset  SREG <- SMEM[baseSREG + imm]
                    ScalarStoreSmemAbsolute  SMEM[abs imm] <- SREG
V5+ (VXC/GXC)       ScalarLoadSmemY       SREG <- SMEM[imm]          (Y = 20-bit imm)
                    ScalarLoadSmemXY      SREG <- SMEM[Xreg + imm]   (base+displacement)
                    ScalarStoreXToSmemY   SMEM[imm] <- Xreg
                    ScalarStoreXToSmemSumDestAndY  SMEM[imm] += Xreg (6acc60406 scatter-add)

XY 形式(X = base SREG,Y = ScalarYEncoding reg/imm/const)是规范 V5+ base+displacement address generator;indicesToOffsetΣ indices[d]·strides[d] 会 lowering 为一串打包进 SPU slots 的 scalar IntegerAdd/Multiply ops。见 Memory-LoadMemory-Store


Immediate Slots

六个 immediate slots 位于每个 bundle 的低端(与 scalar slot 的高端相对)。它们由逐代际 TensorCoreImmediatesEncoder 放置,每个 slot 是一次 BitCopy 调用。宽度和位置按字节精确:

Genslotswidthimm0..imm5 bit offsetsencoder
Jellyfish616 bBundle proto words +0x70/+0x74/+0x78/+0x7c(mask bits 0x1000..0x8000Place16BitScalarImmediate @ 0x1e8721e0
Viperfish620 b330, 350, 370, 390, 410, 4300x1eebee40
Ghostlite620 b333, 353, 373, 393, 413, 4330x1f20d520
6acc60406620 b323, 343, 363, 383, 403, 4230x1f86de20

V5+ slots 是完美的 stride-20 阶梯(BitCopy(span, off, …, 20)),GL 阶梯同样是 VF+3,GF 阶梯是 VF−7 — 与 scalar slot 显示的统一逐代际位移相同。32-bit immediate 消耗相邻的一对 20-bit slots(40 ≥ 32);更宽的值会变成由 SPU 发出的 SMEM constant load。

Jellyfish 的 Place16BitScalarImmediate0x1e8721e0)遍历 slot-presence mask,测试 0x8000/0x4000/0x2000/0x1000,并返回 0x20..0x23 中引用它填入的 slot 的 ScalarYEncoding,以 (val >> 13) & 4 折叠 sign bit。32-bit 值通过 Place32BitScalarImmediate0x1e8724c0)拆分进相邻 slot pair。完整逐代际 encoding-id → slot-position map 见 Immediate Slot


Scalar↔Vector Bridge

SPU 和 vector engine 通过 FIFO 交换值,绝不通过直接 register port — bundle-internal writes 不会 cross-feed(见 Bundle Model)。Vector→scalar 值穿过 V2S FIFO:vector engine push(debug accessor WriteV2SFifo @ 0x0e758400),SPU 用 PopV2s pop(debug ReadV2SFifo @ 0x0e758060)。规范用途是 reduction:vector reduce 把其 scalar result 写入 V2S,SPU 将其 pop 用于 control flow。V5+ 增加 PopDrf(data-result FIFO,MXU/EUP results)、PopSfrf(sync-flag-result FIFO)以及 PopSccf/PushSccf(Viperfish SCCF)— 它们都作为 Viperfish lane-0 switch 中的 dedicated cases(0x3A..0x3E)存在。scalar→vector 方向是单独的 TensorCoreVectorScalar slot(encoder 0x1f01a3e0),它把 SREG 值 broadcast 到 vreg lane。FIFO 还充当 CallSreg 的隐式 return-address stack(return = BranchSreg reading the popped value);任何代际都没有专用 return opcode。


逐代际差异

FeatureJF v2PF v4VF v5pGL v6eGF TPU7x
Scalar lanes22222(+ dual-pred slot)
Opcode field6-bit single5-bit + 6-sub4-class + 6-sub4 + 62 + 6
In-slot predicate5-bit fieldopcode 0x1F=NeverExec1-bit @5031-bit @506none(dual-pred slot)
Jump-table size (lane 0)0..0x3E≤0x33≤0x4C≤0x49≤0x49
SREGs3232323232
Immediate slots6 × 16 b6(Misc/ScalarY)6 × 20 b6 × 20 b6 × 20 b
HW call/returninlined(none)yes(lane 0)yesyesyes
HW loop counter (ReadRegLcc)nonoyesyesyes
HaltYieldnonoyes + ConditionalConditional onlynone
ScalarStoreXToSmemSumDestAndY (lane 1)nonononoyes(scatter-add)
LogicalShiftLeftOnesXByYPlaces (lane 0)nonononoyes
FIFO bridgeV2S, HmfV2SV2S,Drf,Sfrf,SccfV2S,Drf,SfrfV2S,Drf,Sfrf

6acc60406 (GF) TensorCore scalar slot 与 Ghostlite 有两个二进制确认的差异:它向 lane-0 op set 添加 LogicalShiftLeftOnesXByYPlaces(GF lane-0 switch 0x1f87b420 有 65 个 distinct Encode… mnemonics,而 GL 有 64 个;唯一新 case 位于 0x18),并向 lane-1 op set 添加 ScalarStoreXToSmemSumDestAndY(scatter-add store;存在于 GF TensorCoreScalarAlu1 family — 例如 0x1f64dc60 — 但不存在于 VF/GL TensorCore lane-1 store set,后者止于 ScalarStoreXToSmemY)。因此 lane-0 的 65-vs-64 数量只反映 LogicalShiftLeftOnesXByYPlaces;scatter-add store 是单独的 lane-1-only addition,不出现在 lane-0 switch 中。6acc60406 (GF) 还丢弃 in-slot predicate(0x1f87b420 中没有 predicate BitCopy;opcode-class 从 [instr+0x1c] 读取为 2 bits,而不是像 [instr+0x20] 那样读取为 4)。被丢弃的 predicate 路由到 dedicated dual-pred_0/pred_1 slot;从 scalar slot 到该 slot 的确切 wiring 未追踪(见 Limits)。(此 GF 代际是外部文档中的 "Ironwood"/TPU7x;不要与前一代 Ghostlite/v6e,即 Trillium 混淆。)

GOTCHA — 6acc60406 (GF) 从不同 proto offset 读取 opcode class。 VF/GL/SCS encoder 从 [instr+0x20](4 bits)读取 class;GF 从 [instr+0x1c](2 bits)读取。硬编码 +0x20 offset 和 4-bit class 的 decoder 会误读每个 GF scalar slot。该 slot 也在 bundle 中下移约 13 bits(sub-opcode @483 vs GL @496),正是因为 predicate bit 不再存在于 slot 中。

Branch/call discriminators (lane 0)

control-flow ops 共享 sub-opcode/operand grid,但通过写入 opcode-LOW field 的值来区分 — 即表格称为 “sub-opcode discriminator” 的同一个 5-bit field,位于下面的逐代际位置。在 6acc60406 (GF)(0x1f87b420)上,immediate branch/call family 将 6-bit sub-opcode(opcode-HIGH)@ 483 钉为 0,并通过 478 处的 5-bit field 选择 op:

Opopcode value ([instr+0x50])sub-opcode @483 (w6)discriminator @478 (w5)leaf encoder
BranchAbsolute62040x1f87f5c0
BranchRelative63050x1f87f660
CallAbsolute65060x1f87f7e0
CallRelative66070x1f87f8e0

register-indirect forms(BranchSreg/CallSreg)则把其 opcode 携带在 opcode-HIGH(@483)field 中,且 CallSreg 把 return-address SREG 写入 467 处的 5-bit field,并把 link/target SREG 写入 472 处的 6-bit field。这与 Sequencer Slot 从同一组 encoder 记录的 discriminator grid 相同;SPU 页面的 “sub-opcode” 是该页的 “opcode-HIGH”,而 @478 field 是其 “opcode-LOW”。在 Viperfish(0x1eecb900)上,等价字段位于 VF 位置(sub @493,dst @477);在 Ghostlite 上位于 VF+3(sub @496,dst @480)。


SparseCore Scalar Slot

SparseCore sequencer 在 32-byte SCS bundle 中携带自己的 scalar lanes(SparseCoreScalarAlu0/1)。Viperfish SCS encoder(vfc::isa::SparseCoreScalarAlu0Encoder::Encode @ 0x1ee82ce0)在结构上与 TensorCore lane-0 encoder 相同 — 同样读取 [instr+0x20] class、同样读取 [instr+0x18] predicate、同样从 [instr+0x50] dispatch — 但缩放到 32-byte bundle:

c
// vfc::isa::SparseCoreScalarAlu0Encoder::Encode  @ 0x1ee82ce0 (lines 27-46)
v22[0] = *(int*)(instr + 32);  BitCopy(span, 187, v22, 0, 4);    // opcode-class @bit 187, 4b
v22[0] = *(uint8*)(instr + 24); BitCopy(span, 191, v22, 0, 1);   // predicate    @bit 191, 1b
case 6: BitCopy(span, 181, &zero, 0, 6);                         // sub-opcode   @bit 181, 6b
        BitCopy(span, 176, &Xreg, 0, 5);                         // X register   @bit 176, 5b
```text

SCS scalar slot 位于 256-bit bundle 顶部附近(bytes 2223)。它的 jump table 比 TensorCore 的*更大* — cases 到 0x5788 ops)— 因为 SparseCore scalar path 携带 TensorCore 没有的 gather/scatter address ops。Ghostlite 和 6acc60406 SCS 共享 32-byte bundle width,所以预期 byte positions 相同;这是从共享 bundle width 推断而来,未对 GL/GF SCS 按字节验证(MEDIUM)。

---

## 限制和开放项

- **逐 opcode operand sets(Partial)。** binary-ALU template(`IntegerAdd` 类:VF 上 dst@477、X@488、Y@482)、load/store template 和 branch template 已解码。其余约 70 个 VF /64 个 GL /65 个 GF ops 复用同一 field grid,但每个 op 的确切 present/absent operand set 未逐个叶子 encoder 枚举。(非模板 op 的 operand presence 为 LOW。)
- **V5+ predicate-register count(Partial)。** 只有 Jellyfish 的 15-predicate file 已解码;VF/GL/GF subtarget 的 `getNumPredicateRegisters` override 尚未解码 — in-slot 1-bit field 是进入逐代际 predicate file 的 selector,其大小位于 HAL factory 中。见 [Predicate Register File](slot-predicate.md)。
- **Pufferfish immediate geometry(Partial)。** PXC 通过 `TensorCoreMiscEncoder` `ScalarY` 5-bit fields 路由 immediates,而不是干净的 16-bit slot ladder;确切 PXC slot count/width 未钉住(只有 5-bit `ScalarY` field)。表格列出的是 JF/VF/GL/GF immediate ladders,它们是 byte-exact。
- **6acc60406 (GF) dual-predicate routing(Not traced)。** slot 存在;未跟踪从 `ScalarAlu0` 被移除的 predicate bit 到 `TensorCorePredicates` 的 wiring。
- **GL/GF SparseCore slot positions(Inferred)。** 只有 VF SCS layout(`0x1ee82ce0`)按字节验证;GL/GF SCS positions 从共享 32-byte bundle width 推断。
- **Decode side(Located, not field-decoded)。** 已定位对称的 `*ScalarAlu0Decoder` methods,但未逐字段解码;它们是在相同 offset 上的逆向 `BitCopy` extraction。见 [Decode-Side: VF / GXC](decode-side-vf-gxc.md) 和 [Decode-Side: JF / PF](decode-side-jf-pf.md)。

---

## 交叉引用

- [Bundle Model](bundle-model-overview.md) — 逐代际 bundle widths、no-scoreboard VLIW contract,以及本页为 scalar lanes 实例化的 slot taxonomy。
- [Sequencer Slot](slot-sequencer.md) — 绑定到 scalar lane 0 的 branch/call/halt control-flow ops,以及 proto-bundle emitter fold。
- [Immediate Slot](slot-immediate.md) — `ScalarYEncoding` `0x20..0x25` references 解析到的逐代际 encoding-id → immediate-slot bit-position map。
- [Predicate Register File](slot-predicate.md) — in-slot 1-bit predicate selector 索引的(较小)predicate file,以及 JF 5-bit predicate field。
- [Jellyfish Bundle](bundle-jf-41b.md) — 41-byte direct-pack layout,以及本页总结的 scalar-slot lane-0/lane-1 field arithmetic。
- [Ghostlite Bundle](bundle-gl.md) — 相对于 Viperfish 的统一 +3-bit scalar/immediate shift 的来源。
- [6acc60406 Bundle](bundle-gf.md) — 将 scalar predicate 移到 dual-predicate slot 的 GF generation。
- [MC-Emitter](mc-emitter.md) — `getBinaryCodeForInstr` / `InstBits`,V5+ proto encoder 绕过的 LLVM-MC path(全零 `InstBits`)。
- [LLO Opcode Enum](llo-opcode-enum.md) — scalar-ALU opcodes 通过逐代际 jump tables 映射到的 462-opcode LLO enum