Skip to content

MXU 延迟:VF

本页所有地址都适用于 libtpu-0.0.40-cp314 wheel 中的 libtpu.so(build libtpu_lts_20260413_b_RC00,BuildID md5 89edbbe81c5b328a958fe628a9f2207d)。该二进制 strip — 下方每个符号都是 demangled C++ 名称。Section map:.text/.rodata VMA == file offset;.data.rel.ro VMA − 0x200000 == file offset。

摘要

本页是 MxuLatencyTable 预留模型在 Viperfish(v5/v5e)上的具体化:实际的整数 cycle-hold 表体,用于定价一个 MXU op 如何占用脉动阵列子单元。概览记录的是跨 gen 不变的索引方案 — 四个 op family、四个 flat_hash_map、一次带逐 gen 默认 fallback 的 find → array[resource] 读取;本页则逐行 dump Viperfish 这个资源最宽世代的值。每个 VF 预留行都是定长 std::array<int,19>(符号字面上是 std::__u::array<int,19ul>,lookup 会检查 resource >= 0x13),每个 int 对应一个 MxuResource 微流水端口。

参照框架仍是专用于一个功能单元的 LLVM WriteRes/ProcResource 表:每个 MXU op 为若干周期预留 19 个 MXU 子端口中的一组,背靠背发射停顿由仍被持有且最忙的端口门控。Viperfish 表由一个约 27 KB 的构造函数(MxuLatencyTable::MxuLatencyTable @0x1c8a52c0)构建一次,它填充四张 map — matpush(latch/matprep)、matmul、matres(result read)和 vlxmr(vector-latch-into-MRB)— 通过 SetReservations<Modifier> 将小型稀疏 {MxuResource → cycles} 集合稠密化。matpush map 最丰富:32 个 key = 8 个 MatmulDataFormat dtype × {NoXpose, Xpose} × {Msr0, Msr1},除 f32 非转置半成本情形外几乎统一定价。

本页逐值覆盖四个 family 的 value matrix、19 列 MxuResource band 分配(由 pin 住特定列的 consumer accessor 锚定)、GetResourceUsage dispatch 及其逐资源 default-key 集(res3→15,res11→0),以及 AddOverrunCheckReservations 增强 — 反编译显示它是在八个具名 kMsr{A,B}OverrunCheck{0..3} 列上的四步 {5,13,21,29} ramp,而不是单个值。页面最后说明 MXU1 与第二个物理发射槽的 twin 关系。

对于重新实现,契约是:

  • 四个 flat_hash_map<Modifier, array<int,19>>,以及 GetResourceUsage 为每个 family 构造的 modifier-key。
  • 32 个 matpush 预留行({[0]:4,[push]:3,[latch]:2} 形式,f32-NoXpose {[0]:2,...:1,...:1} 半成本例外)、matres {[18]:8|4} tail、vlxmr {2,6,14,22,30} load-feed ramp,以及 matmul 行的目标列。
  • 19 列 MxuResource band:每个 index 预留哪个端口,由 GetResourceUsage(res11→col0,res3→col15)、GetXluPathReservation(res14)和具名 overrun-check 枚举值(cols 2..9)锚定。
  • 逐资源默认 fallback(res3→15,res11→0),以及 kMsr{A,B}OverrunCheck{0..3} 上的 overrun-check {5,13,21,29} ramp。
Classxla::viperfish::MxuLatencyTable
VF ctorxla::viperfish::MxuLatencyTable::MxuLatencyTable @0x1c8a52c0(约 27 KB)
VF lookupxla::viperfish::MxuLatencyTable::GetResourceUsage @0x1c8ae5c0
预留数组std::array<int,19> — bound resource >= 0x13BUG()
MxuResource 数量19(MxuResource::kNumMxuResources,CHECK 锚定,mxu_latency_table_vf.cc:58
Map 偏移matpush @ this+0x00(key+4 array)· matmul @ this+0x20(key+8 array)
Default-key 集res3 → 15res11 → 0;其他任何 res → InvalidArgument
行构造器SetReservations<{Matpush@0x1c8abde0, Vlxmr@0x1c8accc0, Matres@0x1c8acea0}>
Overrun 增强AddOverrunCheckReservations @0x1c8abfe0{5,13,21,29} on kMsr{A,B}OverrunCheck{0..3}
MXU1 twin第二个 VectorExtended 发射槽,−20-bit control region;共享一个预留后端

预留数组形状

目的

每个 VF 预留行都是一个按 MxuResource 索引的稠密 19-int 向量。op 的预留在构建时是稀疏的(一个 3–6 项 flat_hash_map<MxuResource,int>),但存储为稠密形式:SetReservations 将数组零初始化,只写入具名单元格,因此 op 从未触及的资源持有 0 个周期。

结构

四张 map 位于 MxuLatencyTable 对象中的固定偏移,区分依据是 key 类型(C++ template argument),而不是 discriminator 字段:

text
MxuLatencyTable (heap, reachable at owning LatencyTable + 0x1d8)
  this + 0x00   flat_hash_map<MatpushModifier, array<int,19>>   ── latch / matprep  (key 4 B → array at key+4)
  this + 0x20   flat_hash_map<MatmulModifier,  array<int,19>>   ── matmul           (key 6 B → array at key+8)
  this + ...    flat_hash_map<MatresModifier,  array<int,19>>   ── matrix-result read (key 1 B)
  this + ...    flat_hash_map<VlxmrModifier,   array<int,19>>   ── vector-latch-into-MRB (key 4 B)
```text

`GetResourceUsage` `@0x1c8ae5c0` 读取 matmul map 时将 `a2 + 32`(`this+0x20`)传给 `find<MatmulModifier>`,读取 matpush map 时将裸 `a2`(`this+0x00`)传给 `find<MatpushModifier>` — 两者均有字节确认。value array 会用三次 `vmovups ymm` load(19 ints = 76 bytes ≈ 三个 32-byte load)从 hash bucket 复制到栈上后再索引。复制偏移依赖 key 宽度:8 字节对齐的 `MatmulModifier` bucket 用 `vmovups [rdx+8]`,4 字节 `MatpushModifier` bucket 用 `vmovups [rdx+4]`。

### 行如何构建 — `SetReservations`

`SetReservations<MatpushModifier>` `@0x1c8abde0`(已验证)将稀疏 `{MxuResource → cycles}` map 稠密化为 19-int array 并 `try_emplace`:

```c
function SetReservations<Modifier>(packed_key, resource_to_cycles_map, target_map):  // VF @0x1c8abde0
    array<int,19> res_vector = {0};                  // vxorps + vmovups zero-init, 19 slots
    for (resource_index, cycles) in resource_to_cycles_map:
        if resource_index >= 19:                      // mxu_latency_table_vf.cc:58
            LogFatal(MakeCheckOpString(resource_index, 19,
                     "resource_index < to_underlying(MxuResource::kNumMxuResources)"))
        res_vector[resource_index] = cycles
    if not target_map->try_emplace(packed_key, res_vector).second:   // mxu_latency_table_vf.cc:71
        LogFatal("resource_map->try_emplace(key, res_vector).second")   // keys must be unique

将 19-int array 存入新 bucket 本身也是三次 vmovups 写([rdx+4][rdx+0x24][rdx+0x30])— array 落在 bucket offset 4,位于 4-byte MatpushModifier key 之后。SetReservations<VlxmrModifier> @0x1c8accc0SetReservations<MatresModifier> @0x1c8acea0 在边界(>= 0x13)和两个 CHECK 字符串(vf.cc:58vf.cc:71)上逐字节相同,只是 key 类型不同。三个 CHECK 都在反编译中有字节确认。

NOTE — array 宽度是资源数量,不是行数。Viperfish 上 MxuResource::kNumMxuResources = 19,而 Ghostlite / TPU7x 上是 11。因此两个世代会用同一个整数索引不同的物理子单元,重新实现必须为每个 gen 保持独立的 MxuResource enum。19 由 SetReservations(写侧)和 GetResourceUsage(读侧)中的 resource >= 0x13 边界字节锚定。


Lookup 与 Default-Key 集

算法

GetResourceUsage @0x1c8ae5c0 有字节确认:

c
function MxuLatencyTable::GetResourceUsage(this, instr, resource, is_throughput):  // VF @0x1c8ae5c0
    // 1. Per-resource default fallback, taken before any table lookup.
    if resource == 3:    default_cycle = 15           // VF latch/issue-slot seed
    elif resource == 11: default_cycle = 0            // VF matmul-issue slot seed
    else:                                             // status path, vf.cc
        return InvalidArgument("Unsupported kind of resource")
    if instr > 0x10A:  goto matpush_dispatch          // opcodes above the matmul band

    // 2. Matmul family — key byte[0] = MatmulDataFormat, byte[1..] = 0; map @ this+0x20.
    switch (instr):
        case 212: key = MatmulModifier{format=1};  if find(this+0x20, key): read array[resource]
        case 218: key = MatmulModifier{format=2};  ...
        case 230: key = MatmulModifier{format=6};  ...
        default:  goto matpush_dispatch

  matpush_dispatch:
    // 3. Matpush family — key from the latch mode, map @ this+0x00.
    switch (instr):
        case 267: key = MatpushKey(latch_mode)            // plain matpush
        case 271: key = MatpushKey(latch_mode ^ 0xB)      // xpose pass
        case 277: key = MatpushKey(latch_mode | 0x14)     // wide pass
        default:  LogFatal("Unsupported opcode")          // vf.cc:578
    if not find(this+0x00, key): throw out_of_range       // raw_hash_map::at

    // 4. Read.
    array<int,19> res_vector = entry.value                // vmovups copy ([rdx+8] matmul / [rdx+4] matpush)
    if resource >= 19: BUG()                              // bound 0x13
    return res_vector[resource]
```text

matpush key 由三个 helper 组装:`byte[0] = GainLatchModeToMatmulDataFormat(latch_mode)`(`@0x1d629260`)、`byte[1..2] = LatchModeIsTranspose(latch_mode)`(`@0x1d628ea0`)、`byte[3] = LatchOpcodeToMsr(0x8F)`(`@0x1c8a1300`)。opcode 特定的预变换 — opcode 271 的 `^0xB`、opcode 277 的 `|0x14` — 将同一物理 latch 路由到 transposed 或 wide bucket。

> **NOTE —** 逐资源 default key 是**逐世代**的。Viperfish 上是 `resource==315` 和 `resource==110`(`@0x1c8ae5c0` 处 `a4==3 → v9=15`、`a4==11 → v9=0`);Ghostlite/TPU7x 上会移动到 `resource==43` / `resource==99`。重新实现必须从逐 gen lookup 读取 default-resource key,不能把 VF pair 跨 gen 搬运。除 311 以外的任何 `resource` 返回 `InvalidArgument` Status("Unsupported kind of resource"),而不是越界读取。
>
> **GOTCHA —** matmul opcode 在各 gen 间****稳定。VF 在 opcode 212/218/230(formats 1/2/6)上匹配 matmul,在 267/271/277 上匹配 matpush;Ghostlite 匹配另一组 opcode。按逐 gen opcode list 绑定 family。matmul opcode 230 = `0xE6` 是 `s8` matmul ordinal(format 6 = int8/x8);212 = `0xD4` 是 f32(format 1);218 = `0xDA` 是 bf16(format 2)。

### Function Map

| Function | Address | Role |
|---|---|---|
| `viperfish::MxuLatencyTable::MxuLatencyTable` | `0x1c8a52c0` | VF ctor — 填充全部四张 map(约 27 KB) |
| `viperfish::MxuLatencyTable::GetResourceUsage` | `0x1c8ae5c0` | VF lookup — family dispatch + `find` + `array[resource]` |
| `viperfish::SetReservations<MatpushModifier>` | `0x1c8abde0` | 将 `{res→cyc}` 稠密化为 `array<int,19>`,`try_emplace` |
| `viperfish::SetReservations<VlxmrModifier>` | `0x1c8accc0` | vlxmr 行构造器(bound 0x13|
| `viperfish::SetReservations<MatresModifier>` | `0x1c8acea0` | matres 行构造器(bound 0x13|
| `viperfish::AddOverrunCheckReservations` | `0x1c8abfe0` | 插入 `kMsr{A,B}OverrunCheck{0..3}` → `{5,13,21,29}` |
| `GainLatchModeToMatmulDataFormat` | `0x1d629260` | matpush key byte[0] |
| `LatchModeIsTranspose` | `0x1d628ea0` | matpush key byte[1..2] |
| `LatchOpcodeToMsr` | `0x1c8a1300` | matpush key byte[3] |
| `viperfish::MxuLatencyTable::MxuOpResourceReservations` | `0x1c8ad080` | 累积窗口内逐资源预留 |
| `viperfish::MxuLatencyTable::MxuOpHoldIssues` | `0x1c8ad3a0` | 背靠背发射停顿递推 |
| `LatencyTableViperfish::GetXluPathReservation` | `0x1c8a3200` | 通过 `ViperfishPerformance` res 0x0e 读取 Xlu deposit(锚定 `MxuResource` col 14|

---

## 19 个 MxuResource 列

### 目的

19 个 `MxuResource` 索引是 MXU 微流水预留端口。枚举(`xla::viperfish::MxuResource`)在二进制中没有 `ToString`,因此多数列按其 writer-modifier 角色进行功能性命名。若干列由直接 accessor 或 `try_emplace` CHECK-string 证据钉住,置信度为 CERTAIN。

### 列

| col | name | anchor | written by |
|---|---|---|---|
| 0 | MXU issue / dispatch port | `GetResourceUsage` `ViperfishPerformance::Resource 0xb → col0` | matpush, matmul |
| 1 | vlxmr load-feed stage 0 | ctor vlxmr map | vlxmr (No + Xpose) |
| 2 | `kMsrAOverrunCheck0` | `AddOverrunCheckReservations` CHECK `vf.cc:78` | overrun (MSR-A), matmul |
| 3 | `kMsrAOverrunCheck1` | overrun CHECK `vf.cc:80` | overrun (MSR-A) |
| 4 | `kMsrAOverrunCheck2` | overrun CHECK `vf.cc:82` | overrun (MSR-A), matmul |
| 5 | `kMsrAOverrunCheck3` | overrun CHECK `vf.cc:84` | overrun (MSR-A), matmul |
| 6 | `kMsrBOverrunCheck0` | overrun CHECK `vf.cc:87` | overrun (MSR-B) |
| 7 | `kMsrBOverrunCheck1` | overrun CHECK `vf.cc:89` | overrun (MSR-B) |
| 8 | `kMsrBOverrunCheck2` | overrun CHECK `vf.cc:91` | overrun (MSR-B), matmul |
| 9 | `kMsrBOverrunCheck3` | overrun CHECK `vf.cc:93` | overrun (MSR-B) |
| 10 | matpush PUSH port — MSR bank 0 | ctor `Msr0` push pair | matpush(Msr0), matmul |
| 11 | matpush PUSH port — MSR bank 1 | ctor `Msr1` push pair | matpush(Msr1) |
| 12 | matpush LATCH port — MSR bank 0 | ctor `Msr0` latch pair | matpush(Msr0), matmul |
| 13 | matpush LATCH port — MSR bank 1 | ctor `Msr1` latch pair | matpush(Msr1), matmul |
| 14 | Xlu / matrix-result deposit | `GetXluPathReservation` → `ViperfishPerformance` res 0xe | vlxmr(Xpose) |
| 15 | MXU-result port(matmul throughput `{8,16,32}`) | `GetResourceUsage` `Resource 3 → col15`;ctor key `15 → {8,16,32}`(fmt 1/2/6| matmul |
| 16 | MXU-result sub-stage A | ctor matmul map | matmul |
| 17 | MXU-result sub-stage B | ctor matmul map | matmul |
| 18 | matmul-result-feed / accumulate tail | matres row `[18] = 8 \| 4` | matres |

> **NOTE —** Cols 2..9 是 `OverrunCheck` 预留,由 `AddOverrunCheckReservations` `@0x1c8abfe0` 安装:具名枚举值 `kMsrAOverrunCheck{0,1,2,3}`(cols 2,3,4,5)和 `kMsrBOverrunCheck{0,1,2,3}`(cols 6,7,8,9),每个都携带**渐进 ramp `{5,13,21,29}`**(不是平坦值),背后分别有 `vf.cc:78..93` 的不同 `try_emplace(...).second` CHECK 字符串。MSR-A 集合在 `a1 != 1`(MSR0)路径上采用,MSR-B 集合在 `a1 == 1`(MSR1)路径上采用;`a1 ∉ {0,1}` 在 `vf.cc:95` 处是 `LogFatal("Invalid MSR.")`。vlxmr feed 在自己的行中占用 col 1,但 overrun 增强给 cols 2..9 命名。

### MSR bank 选择

matpush PUSH/LATCH 端口遵循 key 的 `Msr` byte:`Msr0 → {push col 10, latch col 12}`,`Msr1 → {push col 11, latch col 13}`。Viperfish 有两个 matrix-staging-register bank(`ViperfishTarget::MatrixStagingRegisterCount` `@0x1d49ace0` = 2),因此 latch 上的 1-bit `Target` 字段(在 bundle abs 58 解码,见 [`slot-mxu`](../isa/slot-mxu.md))选择哪个 bank,该 bank index 随后成为 key byte[3] = `LatchOpcodeToMsr(0x8F)`。

---

## Matpush 预留行

### 目的

matpush(latch/matprep)family 是移动 operand 的阶段;它的预留决定背靠背 latch 发射的门控。VF ctor 构造 32 个 key:外层循环遍历 `kMsrs = {0, 1}`(`@0xb43b1df`,loop base `+0x21`),内层循环遍历格式表 `{2,3,4,5,6,7,8}`(`@0xb43b1d8`,bf16/f8e5m2.bf16/f8e4m3b11.bf16/u8/s8/u4/s4),另加直接构造的 f32 key — 每个都与 {NoXpose, Xpose} 交叉。

### Value matrix

32 行可归约为一条规则加一个例外。每个 key 都得到 `{[0]:4, [push]:3, [latch]:2}`,**** `{f32, NoXpose, *}` 得到半成本 `{[0]:2, [push]:1, [latch]:1}`:

```text
array(fmt, xpose, Msr) = (fmt == 1 (f32) && !xpose)
    ? { [0]:2, [10+Msr]:1, [12+Msr]:1 }     // f32 NoXpose — HALF cost
    : { [0]:4, [10+Msr]:3, [12+Msr]:2 };    // everything else
where push-col = 10+Msr, latch-col = 12+Msr  (Msr ∈ {0,1})
key ({fmt,xpose,0,Msr})fmtxposeMsrarray<19> non-zero cells
0x000000011 (f32)no0[0]=2 [10]=1 [12]=1(half)
0x010000011 (f32)no1[0]=2 [11]=1 [13]=1(half)
0x000001011 (f32)yes0[0]=4 [10]=3 [12]=2
0x010001011 (f32)yes1[0]=4 [11]=3 [13]=2
0x000000NN2..8no0[0]=4 [10]=3 [12]=2
0x010000NN2..8no1[0]=4 [11]=3 [13]=2
0x000001NN2..8yes0[0]=4 [10]=3 [12]=2
0x010001NN2..8yes1[0]=4 [11]=3 [13]=2

32 个 key 覆盖 fmt ∈ {1..8}NN = 02..08)。格式码是 MatmulDataFormat:1 f32,2 bf16,3 f8e5m2.bf16,4 f8e4m3b11.bf16,5 u8,6 s8,7 u4,8 s4。fmt 9(f8e5m2)和 fmt 10(f8e4m3fn)没有 VF matpush key — ctor 格式表到 8 为止;这些 packed FP8 格式仅属于 Ghostlite/TPU7x。

QUIRK — 概览描述的逐 dtype throughput delta 存在于 VF matpush 预留行中。在 Viperfish 上,除 f32-NoXpose 外每个 matpush key 都支付平坦 {[0]:4, [push]:3, [latch]:2}(f32-NoXpose 半成本为 {[0]:2, [push]:1, [latch]:1});matpush dtype 只在 f32-vs-rest × transpose 上分叉,因此 array[0]MxuResource 0,issue/dispatch port)是平坦 4(或 f32-NoXpose 的 2)— 不是逐格式 {2,4,8} ramp(ctor @0x1c8a52c0 lines 650/683 处 *(_BYTE *)v998 = 0; LODWORD(v998[1]) = {2|4})。逐格式 throughput 幅度 {8,16,32}(format 1/2/6 → 8/16/32)反而落在 matmul 预留数组的 array[15]MxuResource 15,MXU-result port)中,在此 ctor 中字节锚定:*(_BYTE *)v145 = 15; *((_DWORD *)v145 + 1) = 8(line 1466/1467,fmt 1)、15 → 16(line 1946/1947,fmt 2)、15 → 32(line 2855/2856 和 3287/3288,fmt 6)。它也镜像在 ViperfishPerformance grid col r3 中(见 performance-vf)。把逐格式 scaling 放进 matpush 预留的重新实现会在 VF 上过度计费 integer latch — 逐格式 scaling 应属于 matmul array[15]


Sibling Families — Matres、Vlxmr、Matmul

Matres — result-read tail

MatresModifier 是 1-byte {fmt} key(array 在 key+4 读取)。整行只有 col 18 一个 tail 单元格,按 dtype 分叉:

key / fmtdtypearray<19>
0x01..0x04f32 / bf16 / f8e5m2.bf16 / f8e4m3b11.bf16[18] = 8
0x05..0x08u8 / s8 / u4 / s4[18] = 4

matmul-result tail 对宽/decompose dtype 将 result-feed port(col 18)持有 8 个周期,对 integer dtype 持有 4 个周期 — integer accumulate retire 速度快一倍。

Vlxmr — vector-latch-into-MRB feed

VlxmrModifier 是 4-byte {fmt, xpose, 0, Msr} key(形状与 matpush 相同;array 在 key+4)。VF ctor 只构建两个 key — 比 32-key matpush family 更粗的维度 — 行是单调 load-feed ramp:

keyfmtxposeMsrarray<19>
0x000000000no0[1]=2 [2]=6 [3]=14 [4]=22 [5]=30
0x000001011yes0[1]=2 [6]=6 [7]=14 [8]=22 [9]=30 [14]=33

NoXpose feed 用 {2,6,14,22,30}(从 6 开始 step-8)ramp 填充 cols 1..5;Xpose 变体把 ramp 移到 cols 1,6..9,并添加 [14]=33,即 Xlu/matrix-result deposit(col 14 — GetXluPathReservation 读取的同一端口)。Xpose vlxmr 是唯一触及 Xlu cross-lane port 的 family member。

NOTE — overrun-check 插入(cols 2..9 上的 {5,13,21,29})与 NoXpose-vlxmr feed(cols 2..5)和 Xpose-vlxmr feed(cols 6..9)写入的相同 MxuResource 列重叠。这些是不同 map entry(vlxmr vs matmul-augmented),因此不会在同一行中冲突;列复用反映的是 MSR overrun-check 端口和 vlxmr feed stage 是同一物理子流水在不同占用深度上的表现。

Matmul — pure-matmul 预留

MatmulModifier 是 6-byte {fmt, ..., gains} packed key — 比 4-byte matpush/vlxmr 和 1-byte matres key 更宽 — 因此其 array 在 key+8 读取(lookup 中的 vmovups [rdx+8]),而其他 family 在 key+4。GetResourceUsage 构造 lookup key 时只设置 byte[0]=fmt(opcodes 212/218/230 对应 1/2/6);ctor 构建 12 个 key,b2 ∈ {0,1} 且 gains bit b5 ∈ {0,1}。pure-matmul 行目标列为 {0, 2, 4, 5, 8, 10, 12, 13, 15, 16, 17} — issue port(0)、overrun-augmented MSR ports(2,4,5,8)、push/latch banks(10,12,13)和三个 mxu-result ports(15,16,17)。Col 15 携带字节锚定的逐格式 throughput 幅度 array[15] = {8,16,32},对应 formats 1/2/6(ctor @0x1c8a52c0:1466/1947/2856/3288),也就是 GetResourceUsageresource==3 → 15 default path 上返回的单元格。

GOTCHA (matmul values — partly pinned) — matmul array[15] throughput 单元格是字节锚定 CERTAIN:ctor 为 formats 1/2/6 写入 key MxuResource 15 → {8,16,32}@0x1c8a52c0:1466*(_BYTE *)v145 = 15; *((_DWORD *)v145 + 1) = 8:1947= 16:2856/:3288= 32),而 GetResourceUsageresource==3 → 15 default path 上精确读取此单元格。其余 MatmulModifier 单元格(push/latch banks 10/12/13、MSR overrun ports 2/4/5/8、mxu-result sub-stages 16/17)由 find_or_prepare_insert_large<MatmulModifier>@0x1c8af3e0)内联构建,复制经 AddOverrunCheck 增强的输入 map;这条路径难以完整静态提取值 — KEY 结构(6-byte,array at +8)、目标列集合和 overrun 增强(按 MSR bank 在 cols 2..9 上的 {5,13,21,29})已 CONFIRMED,但那些非 [15] 单元格整数为 MEDIUM,重新实现者应从 ctor 双重循环重新推导。matmul latency(脉动深度,而非 occupancy)存在于 ViperfishPerformance grid(bf16 base 121/131)中。


Overrun-Check 增强

AddOverrunCheckReservations @0x1c8abfe0 在每次 matmul-map insert 前对每个 MSR bank 调用一次,用 matpush overrunning staging register 的结构冒险成本增强输入 {MxuResource → int} map。反编译显示它不是单个值 — 而是在具名枚举值下插入四步 ramp:

c
function AddOverrunCheckReservations(msr, reservation_map):   // VF @0x1c8abfe0
    if msr == 1:                                               // MSR-B bank
        reservation_map.try_emplace(kMsrBOverrunCheck0,  5)    // vf.cc:87, col 6
        reservation_map.try_emplace(kMsrBOverrunCheck1, 13)    // vf.cc:89, col 7
        reservation_map.try_emplace(kMsrBOverrunCheck2, 21)    // vf.cc:91, col 8
        reservation_map.try_emplace(kMsrBOverrunCheck3, 29)    // vf.cc:93, col 9
    elif msr == 0:                                             // MSR-A bank
        reservation_map.try_emplace(kMsrAOverrunCheck0,  5)    // vf.cc:78, col 2
        reservation_map.try_emplace(kMsrAOverrunCheck1, 13)    // vf.cc:80, col 3
        reservation_map.try_emplace(kMsrAOverrunCheck2, 21)    // vf.cc:82, col 4
        reservation_map.try_emplace(kMsrAOverrunCheck3, 29)    // vf.cc:84, col 5
    else:
        LogFatal("Invalid MSR.")                               // vf.cc:95
```text

`{5,13,21,29}` ramp(step 8)是逐 K-tile overrun 成本:overrun staging register 的 matpush 会在更深 overrun depth 0..3 上逐渐更久地持有 overrun-check port(5132129 cycles)。每个 `try_emplace` 都由自己的 `.second` CHECK 保护,因此同一个 overrun-check resource 不会被插入到一个 matmul 行两次。这是 matmul 预留的结构冒险半边,被折叠进 matmul map,使 consumer 将其视为普通 array 的一部分。

---

## MXU1 Twin 关系

Viperfish 通过**两个** `VectorExtended` bundle slots(MXU0 / MXU1)发射 MXU ops,它们是在一个共享 8×6-bit operand pool 上的固定 −20-bit control-region twin([`slot-mxu`](../isa/slot-mxu.md),[`decode-side-vf-gxc`](../isa/decode-side-vf-gxc.md))。成本模型不会按 slot 复制预留表:`MxuLatencyTable` 以 `MatmulDataFormat`/`GainLatchMode` modifier 为 key,而不是以哪个物理 slot 或四个物理 MXU array 中的哪个发射了 op 为 key。MXU1 和 MXU0 op 都解析为针对同一四张 map 的同一个 `find → array[resource]` 读取。

slot-vs-array 正交性(两个 control slots,`mxu_count = 4` 个物理 array)是 bundle encoder 的职责;预留模型将其折叠为 modifier key。`MxuOpResourceReservations` `@0x1c8ad080` 累积一个 instruction window 跨两个 slot 施加的逐 `MxuResource` 预留,`MxuOpHoldIssues` `@0x1c8ad3a0` 计算下一条 MXU op 在仍被持有的资源下会停顿多少周期 — 也就是 [概览](mxu-latency-overview.md) 和 [`mxu-opholdissues-stall`](mxu-opholdissues-stall.md) 详述的停顿递推。

> **NOTE —** 因为 matpush 只持有 push/latch port 13 个周期(`{2,1,1}`/`{4,3,2}` 行),而 systolic matmul latency 是数百周期,VF 上背靠背 bf16 latch stream 以接近 issue-rate 流水;int8 路径在 VF 上的预留同样是 `{4,3,2}`(dtype scaling 存在于 `ViperfishPerformance` grid),因此仅 matpush *occupancy* 不会让这一代上的 int8 相对 bf16 降速 — 这是 VF 特有的 quirk,不同于 [概览](mxu-latency-overview.md) 为一般 modifier 模型描述的 `{8,7,6}` scaling。

---

## Related Components

| Name | Relationship |
|---|---|
| `mxu-latency-overview` | 本页整数所填充的跨 gen 不变索引方案 |
| `matmul-mode-modifiers` | `MatmulDataFormat`/`MatpushModifier` key bytes 和 format → group binding |
| `performance-vf` | 独立的 `ViperfishPerformance` grid(28 cols)— 与此 `array<int,19>` 不同的逐格式 `{8,16,32}` matmul throughput cells |
| `resource-enum` | 更高层 23-slot `ResourceVector` — 不同于 `MxuResource` |
| `mxu-latency-gl` / `-gf` / `-pf` | `array<int,11>`(GL/GF)和 PF sibling reservation matrices |
| `mxu-opholdissues-stall` | 消费这些 array 的 issue-stall 递推 |

---

## 交叉引用

- [MXU 延迟概览](mxu-latency-overview.md) — `MxuLatencyTable` 模型、四个 family、以及本页为 VF 具体化的 `find → array[resource]` 读取
- [MatmulMode & Modifiers](matmul-mode-modifiers.md) — `MatmulModifier`/`MatpushModifier` key bytes、`MatmulDataFormat` codes,以及 format → reservation-group binding
- [MXU 延迟:PF](mxu-latency-pf.md) — Pufferfish 预留矩阵(单 MSR,无 Target 字段)
- [MXU 延迟:GL (Ghostlite)](mxu-latency-gl.md) — 带 shifted defaults(res4→3,res9→9)的 `array<int,11>` 预留矩阵
- [MXU 延迟:GF (TPU7x)](mxu-latency-gf.md) — TPU7x `array<int,11>` 预留矩阵
- [Performance: VF](performance-vf.md) — 28 列 `ViperfishPerformance` grid、matmul throughput cells,以及与此预留表共存的 res 0x0e Xlu deposit
- [Resource Enum (23-slot)](resource-enum.md) — 逐 bundle `ResourceVector`,不同于 MXU 内部的 `MxuResource`
- [MXU Slot](../isa/slot-mxu.md) — 此表定价的 latch/matmul ops 所属 LLO MXU instruction slot;输入 matpush key 的 Transpose/Target latch bits
- [Decode-Side: VF / GXC](../isa/decode-side-vf-gxc.md) — MXU1 −20 twin,以及 matpush key 读取的 abs 57/58 Transpose/Target fields
- [MxuOpHoldIssues 停顿递推](mxu-opholdissues-stall.md) — `MxuOpResourceReservations`/`MxuOpHoldIssues` 如何把这些 array 转换为发射停顿