Skip to content

VC-Balance 分配

本页所有地址均适用于 libtpu-0.0.40-cp314-cp314-manylinux_2_31_x86_64 wheel 中的 libtpu.so(构建 libtpu_lts_20260413_b_RC00,build-id 89edbbe81c5b328a958fe628a9f2207d)。该二进制带有完整 C++ 符号(.text VMA == 文件偏移);下文每个地址都是 VMA。其他版本会不同。

摘要

ICI fabric 是一个扭曲环面,而环面会发生死锁。如果每个芯片都按维度顺序路由,且一个数据包可以在任意链路上使用任意虚拟通道,那么回绕边会闭合一个循环的通道依赖图:一圈数据包各自持有一条链路的缓冲区并等待下一条链路,可能永远停滞。libtpu 用经典的 dateline / VC-class discipline 打破该循环:数据包只有在跨过指定的回绕边界(dateline)之后,才可以向上进入更高的虚拟通道,因此通道依赖图在构造上就是无环的。本页记录实现该 discipline 的分配规则:静态路由的每个非终止 hop 如何被分配 VC ∈ {0,1,2},为什么该分配不会死锁,以及逐轴负载均衡阈值如何把受控比例的流量引到高 VC 上,从而均衡每个 VC 的链路利用率。

该规则是 RoutingTableGenerator::GetNextHopAction @ 0x1fbda6a0 内部的 3 路优先级级联,由 hop 和路径上的三个谓词驱动:路由是否会在下一个芯片处转向Direction::IsSame),此 hop 是否跨过 datelineCrossesDateline),以及 VC 负载均衡是否在此 hop 上触发(GetVcBalanceUsage)。转向强制使用低 VC(1);直行 hop 上的 dateline-cross 或 balance trigger 强制使用高 VC(2);普通直行 hop 保持默认 VC(0)。dateline 谓词是逐轴回绕边界的 side-flip 测试;balance 谓词受逐轴 hop-count 阈值控制,该阈值由 CreateVcBalanceThreshold @ 0x1fbd8320 构造,大致按 ~0.2 · axis_size 缩放。

本页负责说明无死锁论证、CrossesDateline side-flip 谓词、GetVcBalanceUsage gate,以及 CreateVcBalanceThreshold 构造数学,也就是补充 GetStaticPath 的路由生成器切片。产生路径、并由其形状输入此规则的路由生成器(torus-vs-mesh 的逐轴距离选择,以及 (hop_count<<6 | polarity<<3 | orientation) DirectionHops 打包)记录在那一页。最后一节覆盖 multipod 跨 pod 路由发射,它有意使用此级联,而是把跨 pod VC 固定为常量,因为点到点光链路没有需要打破的环面循环。

重新实现时,契约是:

  • 3 路 VC 级联GetNextHopAction 中的精确分支顺序(turn ⇒ VC1;straight + dateline ⇒ VC2;straight + balance ⇒ VC2;否则 VC0)以及结果结构的 VC 字段。
  • dateline 谓词CrossesDateline 中的逐轴 side-flip 测试(两种形式:固定的 axis_size-1 边界,或显式的逐轴 dateline 坐标),以及对逐 hop crossing 取 OR 的路径遍历重载。
  • balance gate 与阈值GetVcBalanceUsage 中的四路 gate 链,以及 CreateVcBalanceThreshold 构造的 ~0.2 · axis_size 逐轴阈值,包括其三路轴 “kind” 选择器。
  • 无死锁不变量 — 为什么“回绕时单调增加 VC”会让通道依赖图无环,以及为什么 multipod 层可以跳过它。
VC 级联RoutingTableGenerator::GetNextHopAction @ 0x1fbda6a0(结果 +0x10 处的 VC 字段)
Dateline 谓词 (Coord,Coord)RoutingTableGenerator::CrossesDateline @ 0x1fbdb120
Dateline 谓词 (Coord,DirectionHops)RoutingTableGenerator::CrossesDateline @ 0x1fbdd200
Balance gateRoutingTableGenerator::GetVcBalanceUsage @ 0x1fbdb4c0
阈值构造RoutingTableGenerator::CreateVcBalanceThreshold @ 0x1fbd8320
转向谓词slice_builder::Direction::IsSame @ 0x20c025e0
VC 数量3 ({0, 1, 2});VC0 默认,VC1 “low”(turn),VC2 “high”(wrap / balance)
来源 (slice_builder)platforms/accel_ssw/deepsea/slice_builder/friends/routing_table_generator.cc
来源 (multipod)platforms/accel_ssw/deepsea/dragonfish/multipod/routing_table_generator.cc

无死锁不变量

目的

ICI 拓扑是一个(扭曲)环面:每条轴都会回绕。维度顺序路由(DOR)会先沿轴 0 完整路由,再沿轴 1,依此类推;它消除了 mesh 内由转向引入的循环,但环面的回绕边会在每个环上重新引入循环。Dally 和 Seitz 提出的标准修复方法是,把每条物理链路划分为多个虚拟通道,并为每个环指定一条 dateline:数据包在跨过 dateline 前使用低 VC 类,跨过后在该轴剩余部分使用高 VC 类。因为数据包的 VC 类只会在跨过 dateline 时增加,永不减少,所以在 VC 扩展后的通道依赖图中无法形成循环等待。libtpu 精确实现了这一点,并额外把一个细节(负载均衡偏移)折入高 VC。

不变量表述

text
For every non-terminal hop of every static route:
  VC class is monotone non-decreasing along the path, and strictly increases
  exactly when the hop crosses a dateline (a wrap edge). A turn drops to VC1
  but a turn in DOR only happens between completed axes, so it never re-enters
  a ring already traversed on a higher VC.

  ⇒ The channel-dependency graph over (physical link × VC) is acyclic.
  ⇒ The fabric is deadlock-free under DOR.
```text

三个 VC 类映射如下:**VC0** 是直行延续的默认值(尚未回绕的 DOR run);**VC1**(“low”)由*转向*强制使用,即路由结束一条轴并开始下一条轴的点;**VC2**(“high”)是 dateline 之后的类,只要 hop 跨过回绕边就会被强制使用。负载均衡触发器会为一部分*直行、非回绕* hop 复用 VC2(见 [balance gate](#balance-gate)),纯粹用于均衡每个 VC 的链路负载;它不影响无死锁论证,因为这些 hop 被 gate 限制为*也*是 dateline hop(balance gate 的第一个条件是 `CrossesDateline`),因此它们本来就合法地处于高 VC 类。

> **NOTE —** multipod 层([如下](#multipod-跨-pod-路由发射))从 `GeneratesDeadlockFreeTables` 返回 `false`,并在跨 pod hop 上使用*固定* VC=1。这并不违反该不变量,因为跨 pod 光链路是点到点链路,没有环面环,因此没有需要打破的循环。pod 内无死锁完全来自此级联生成的逐 pod slice-builder 表。

---

## 3 路 VC 级联

### 目的

在 unicast emitter 解析出非终止 hop 的 `output_link`(通向下一个芯片)后,`GetNextHopAction` @ `0x1fbda6a0` 必须为该 hop 分配 VC `∈ {0,1,2}`。该选择是在三个谓词上的严格优先级级联,三个谓词都针对同一个 hop 求值。结果会写入 action struct 中 `+0x10` 处的 VC 字段(旁边还有 `next_chip @ +8` 和 `output_link @ +0xc`)。

### 入口点

```text
RoutingTableGenerator::GetNextHopAction (0x1fbda6a0)
  ├─ CrossesDateline(src, path)              (0x1fbda8b5 → 0x1fbdb120)  ── crossed?
  ├─ Direction::IsSame(this_dir, next_dir)   (0x1fbda8fd → 0x20c025e0)  ── turned?  (FALSE ⇒ turn)
  ├─ GetVcBalanceUsage(src, dir_hops)        (0x1fbda921 → 0x1fbdb4c0)  ── balance?
  └─ select VC ∈ {0,1,2}, write {next_chip+8, output_link+0xc, vc+0x10}  (0x1fbdaa6a)

算法

c
function VcForHop(gen, src, path, hop):                   // inside GetNextHopAction @0x1fbda6a0
    crossed = CrossesDateline(src, path)                  // 0x1fbda8b5 → 0x1fbdb120
    turned  = Direction::IsSame(this_dir, next_out_dir)   // 0x1fbda8fd → IsSame @0x20c025e0
              // IsSame compares two proto Direction {orientation,polarity} 8-byte structs;
              // FALSE ⇒ the next hop's outgoing direction differs ⇒ the path TURNS here.
    balance = GetVcBalanceUsage(src, this_dir_hops)       // 0x1fbda921 → 0x1fbdb4c0

    if not turned:                                        // line 0x1fbda9cb (v16 = 1)
        return 1   // "Turned, forced to low VC!"          [routing_table_generator.cc:251]
    else if crossed:                                      // line 0x1fbda950 (v16 = 2)
        return 2   // "Crossed a dateline, forced to high VC!"  [:254]
    else:
        vc = 0                                            // line 0x1fbda9ea (v16 = 0)
        if balance:                                       // line 0x1fbda9fd (v16 = 2)
            vc = 2   // "VC load balancing, forced to high VC!"  [:257]
        return vc
```text

> **QUIRK —** 第一个分支在 `!IsSame` 时触发,却记录 *"Turned, forced to low VC"*。`IsSame` 是此 hop 方向与下一个 hop 输出方向之间的*同方向*测试;因此 `!IsSame` 表示路由在下一个芯片处改变轴或极性,即发生**转向**。如果重新实现者把该分支读成“如果方向相同,则强制 VC1”,就会把规则反过来,并在负载下产生会死锁的 fabric。VC0 是*无标签*的直行延续默认值;VC2 是唯一的 “high” VC,可通过两种不同方式到达:dateline 回绕(死锁打破点)或直行 hop 上的 balance overflow。

### VC 表

| 条件(每个非终止 hop) | VC | VLOG 标签(源代码行) |
|---|---|---|
| 直行 (`IsSame`),无 dateline,无 balance | 0 | *(无标签 — 默认)* |
| 转向(`!IsSame` this-vs-next direction) | 1 | `Turned, forced to low VC!` (`:251`) |
| 直行,跨过 dateline(回绕边) | 2 | `Crossed a dateline, forced to high VC!` (`:254`) |
| 直行,无 dateline,balance 触发(`hops ≤ threshold`) | 2 | `VC load balancing, forced to high VC!` (`:257`) |
| `dst` 处终止(`src == dst`,无 hop) | 1 | *(通过 `SetUnicastVcControl(.,1)` 直接设置)* |

这三个 VLOG 字符串是三个分支经字节确认的语义标签,逐字取自反编译级联(`GetNextHopAction` `$_2`/`$_3`/`$_4` 日志点)。级联结构(`if !turned … else if crossed … else { vc=0; if balance vc=2 }`)就是精确的反编译控制流。

### 函数映射

| 函数 | 地址 | 作用 |
|---|---|---|
| `RoutingTableGenerator::GetNextHopAction` | `0x1fbda6a0` | 解析 hop + 通过级联选择 VC |
| `slice_builder::Direction::IsSame` | `0x20c025e0` | 比较两个 `proto::Direction`(转向谓词) |
| `RoutingTableGenerator::CrossesDateline` (Coord,Coord) | `0x1fbdb120` | 逐轴回绕边界 side-flip |
| `RoutingTableGenerator::GetVcBalanceUsage` | `0x1fbdb4c0` | 逐轴 hop-count balance gate |

---

## Dateline 谓词

### 目的

`CrossesDateline` 针对单个 hop 的输出方向轴,回答该 hop 是否跨过该轴的回绕边界。它是在级联中强制 VC2 的谓词,也是 balance 测试的第一个 gate。存在两个重载:单 hop 的 `(Coord src, Coord dst)` 形式 @ `0x1fbdb120`,以及路径遍历的 `(Coord src, DirectionHops)` 形式 @ `0x1fbdd200`,后者对沿一条轴的多 hop 方向中的逐 hop crossing 取 OR。

### 算法 — 单 hop

```c
function CrossesDateline(gen, src, dst):                  // 0x1fbdb120
    dim       = orient(dst's direction)                   // dim from the hop's Direction
    applies   = bit(dim-1) of [topo+0x78]                 // per-axis "dateline applies" bitmap
                                                          //   (_bittest64 at line 92; QWORD[a2+15])
    if not applies:
        return false                                      // axis has no dateline → never crosses

    dateline  = [topo+0x90][dim-1]                        // per-axis dateline coordinate (QWORD[a2+18])
    axis_size = [topo+0x48][dim-1]                        // per-axis size (QWORD[a2+9])
    a = dst.GetCoordinate(dim)                            // line 100
    c = src.GetCoordinate(dim)                            // line 105

    if dateline != 0:                                     // explicit dateline coordinate
        side_dst = (a < dateline)                         // line 121 (v18 = v14 < v16)
        side_src = (c < dateline)                         // line 122 (v19 = v40 < v16)
    else:                                                 // implicit boundary = top of axis
        side_dst = (a + 1 == axis_size)                   // line 117 (v18 = v14+1 == v17)
        side_src = (c + 1 == axis_size)                   // line 118 (v19 = v40+1 == v17)

    return side_src != side_dst                           // line 124 (the cmp dl,r9b side-flip)

crossing 测试是纯粹的 side flip:当且仅当 srcdst 位于回绕边界的相对两侧时,hop 跨过边界。当逐轴 dateline 坐标被设置(dateline != 0)时,边界就是该坐标,“side” 是 coord < dateline。当它为零时,边界退化为最后一个索引与索引 0 之间的接缝,通过 coord + 1 == axis_size 检测。[topo+0x78] 处的 bitmap 允许拓扑在不回绕的轴上禁用 dateline(mesh 轴会无条件返回 false)。

GOTCHA — dateline == 0 分支不是“没有 dateline”,而是位于轴顶端的隐式边界。“没有 dateline”的情况是 bitmap 测试(applies == false),它会在任一 side 计算前短路为 false。如果重新实现把零 dateline 坐标当成“disabled”,就会丢失最常见轴布局(dateline 在接缝处)上的所有回绕检测,导致 fabric 对 VC2 使用不足并可能死锁。

算法 — 路径遍历

c
function CrossesDateline(gen, src, dir_hops):             // 0x1fbdd200
    if dir_hops.code < 128:                               // (code >> 6) < 2 → fewer than 2 hops
        return false                                      // line 30: a single step cannot wrap an axis
    pos = src
    crossed = false
    for k in 0 .. (code >> 6) - 2:                        // walk (code>>6)-1 hops (line 89)
        next = topo.Walk(pos, one_step_dir)               // vtable+0xa0, line 49
        if CrossesDateline(src, pos, next): crossed = true; break   // per-hop single-hop test (line 61)
        pos = next
    return crossed                                        // early-true on first crossing
```text

路径遍历重载存在的原因是,打包的 `DirectionHops` 条目可以编码沿一条轴的多个 hop(`hop_count = code >> 6`);如果这些中间步中的*任何*一步跨过回绕边界,该谓词都必须报告 crossing,因此它会对 `Walk` 推进得到的坐标执行 early-true OR。`code < 128` guard 即 `(code >> 6) < 2`:沿一条轴的单个 hop 不可能跨过它自己的回绕边界,所以不遍历就返回 `false`。

### 函数映射

| 函数 | 地址 | 作用 |
|---|---|---|
| `CrossesDateline` (Coord, Coord) | `0x1fbdb120` | hop 轴上的单 hop side-flip |
| `CrossesDateline` (Coord, DirectionHops) | `0x1fbdd200` | 对多 hop 方向中的逐 hop crossing 取 OR |
| `Coordinates::GetCoordinate` || 读取逐轴坐标值 |
| topology `Walk` | `vtable+0xa0` | 沿一个方向前进一步芯片(路径遍历) |

---

## Balance gate

### 目的

VC 负载均衡会把受控比例的** dateline-crossing 流量转移到 VC2,以均衡仅由无死锁 discipline 造成的每 VC 链路负载偏斜(否则无死锁规则会把长回绕 run 固定到 VC2,而其他所有流量都固定到 VC0)。`GetVcBalanceUsage` @ `0x1fbdb4c0` 返回为级联第三个分支上膛的布尔值。它是一个四路 gate:只有 dateline hop 才会 balance,balancing 必须启用,一个 gating 配置标志必须为 clear,并且该 hop 必须是** hop,即它沿该轴的 hop count 不得超过逐轴阈值。

### 算法

```c
function GetVcBalanceUsage(gen, src, dir_hops):           // 0x1fbdb4c0
    if not CrossesDateline(src, dir_hops):  return false  // line 17/18 — only dateline hops balance
    if gen.vc_balance_enabled != 1:         return false  // BYTE[gen+0x9] == 1 gate (line 20)
    if gen.flag_0x14 | crossed_flag:        return false  // !(v15 | BYTE[gen+0x14]) (line 20)
    orient = dir_code & 7                                 // axis index (line 24)
    hops   = (int)dir_code >> 6                            // signed hop count, sar (line 32)
    return hops <= gen.vc_balance_threshold[orient - 1]    // setle (line 32); vector<int> @gen+0x60

gate 链来自反编译函数的字节精确结果:

  1. CrossesDateline(src, dir_hops) — 路径遍历重载;非 dateline hop 永不 balance(它们已经是 VC0,移动它们不会破坏什么,但也没有帮助)。如果为 false,立即返回 false。
  2. BYTE[gen+0x9] == 1vc_balance_enabled,构建/配置 bool。如果 balancing 关闭,返回 false。
  3. !(crossed_flag | BYTE[gen+0x14]) — 第二个 gating bool,与 crossing 结果组合;只要其中任一被置位,balancing 就被抑制。
  4. hops ≤ threshold[orient-1] — 逐轴上限。orient 是打包方向码的低 3 bit;hops 是该代码的算术右移结果(沿该轴的有符号 hop count)。阈值数组位于 gen+0x60(一个 vector<int>,边界为 gen+0x68)。

NOTE — gen+0x14 处的字节在这里被读取,也被 GetStaticPath 作为其 use_limited_ici mode bit 读取。尚未消歧这是一个真正复用的字段(“limited routing disables balance”),还是两个相邻字段在读取上塌缩到同一处;两个代码路径都读取 BYTE[gen+0x14]。(字段双重角色为 LOW confidence;两个读取都指向 +0x14 为 CERTAIN。)

为什么阈值限制的是 hop

gate 只在 hops ≤ threshold 时触发,即针对 dateline hop。结合阈值的 ~0.2 · axis_size 缩放(如下),这意味着大约最短的五分之一 dateline-crossing 距离会被 balance-shift 到 VC2,而长回绕 run 本来就会停留在 VC2(通过 dateline 分支);因此净效果是把短回绕流量分摊到 VC0/VC2,并让长回绕流量保持在 VC2。无死锁不变量不受影响,因为每个 balanced hop 按 gate 条件 1 都已经是 dateline hop,因此合法地处于高 VC 类。

函数映射

函数地址作用
GetVcBalanceUsage0x1fbdb4c0四路 balance gate,逐轴阈值比较
CreateVcBalanceThreshold0x1fbd8320构建 gen+0x60 逐轴阈值 vector

阈值构造

目的

CreateVcBalanceThreshold @ 0x1fbd8320 填充 gen+0x60 处的逐轴阈值 vector,供 GetVcBalanceUsage 读取。每条轴获得一个随轴大小线性缩放的整数阈值;缩放常量按轴从三个预计算候选值中选择,选择依据是一个轴 “kind” 字段,fallback 则是直接按该轴计算。

算法

c
function CreateVcBalanceThreshold(gen):                   // 0x1fbd8320
    n = gen.num_dims                                      // QWORD[gen+0x50] (this+10)
    resize(gen.threshold, n)                              // vector<int> @gen+0x60, append zeros (line 53)
    if gen.vc_balance_enabled != 1 or gen.flag_0x14:      // BYTE[gen+9]==1 && !BYTE[gen+0x14] (line 56)
        return ok                                         // balancing off → leave thresholds zeroed

    dim_sizes = topo.GetDimensionSizes()                  // vtable+0x50 (line 58)
    min_dim   = min(dim_sizes)                            // cmp/cmovl keeps the smaller (line 59..353)

    // three candidate thresholds, all round-to-nearest( min_dim * mul + add ):
    t_kind1 = round( min_dim * 0.175 - 0.15 )             // stored var_58 (asm @ line 162-169)
    t_kind2 = round( min_dim * 0.222 - 0.1  )             // stored var_60 (asm @ line 155-161)
    t_kind3 = round( min_dim * 0.207 - 0.2  )             // stored var_50 (asm @ line 146-154)

    for i in 0 .. n-1:                                    // per-axis loop (line 171)
        // skip axes whose axis-property (vtable+0x30) == 2:
        prop = topo.AxisProperty(i)                       // vtable+0x30 (line 178)
        if prop == 2:
            continue                                      // (loop advances to next dim)

        kind = [topo+0xe8] for this axis                  // axis "kind" ∈ {1,2,3} (switch line 229)
        switch kind:
            case 1: threshold[i] = t_kind1                // var_58 (line 244)
            case 2: threshold[i] = t_kind2                // var_60 (line 239)
            case 3:                                       // line 231
                if [topo+0x134] == 1 and i == [topo+0x130]:
                    goto direct                           // a single skipped axis (line 232)
                threshold[i] = t_kind3                    // var_50 (line 234)
            default:
direct:         threshold[i] = round( dim_sizes[i] * 0.145 - 0.3 )  // per-axis, asm @ line 194-204
```text

顶部的归约找到 `min_dim`,即*最小*逐轴大小;循环是 `mov (%rax),%r10d; cmp %esi,%r10d; cmovl %r10d,%esi` (`0x1fbd83d0`),其中 `cmovl` 只在 `r10d` *小于*当前 `esi` 时保留 `r10d`,而最小值会在 `0x1fbd84d0` 重新加载到 `r14d`,供 `vcvtsi2sd` 使用。因此候选阈值随*最短*轴缩放,而不是最长轴。三个候选值都是通过内联 SSE 块计算一次的 `round_to_nearest(min_dim · mul + add)`(`±0.5` magic + `vroundsd …, 0Bh` round-to-nearest)。随后每条轴根据 `[topo+0xe8]` 处的 “kind” 字段选择候选值,并有两个 escape hatch:axis-property 2 会完全跳过一条轴(无 balance),kind 3 下由 `[topo+0x130]`/`[topo+0x134]` 对命名的一条特定轴会落入*直接*逐轴计算,使用该轴自身大小而非 `min_dim`。

缩放乘数(`0.175`、`0.222`、`0.207`,以及 direct 的 `0.145`)把阈值放在大约轴长五分之一处,即 `threshold ≈ round(axis_size · ~0.2const)`,因此 balance gate 只允许最短约 1/5 的 dateline-crossing 距离进入。所有四组 `mul`/`add` 都字节锚定到其 rodata double(kind 1/2/3 为 `min_dim · {0.1750.15, 0.2220.1, 0.2070.2}`,direct 路径为 `axis_size · 0.1450.3`);下方 multiplier-to-kind 映射遵循反编译函数体中的 `var_58`/`var_60`/`var_50` switch arm。

### 轴 kind 选择器

|kind (`[topo+0xe8]`) | 阈值来源 | 缩放 |
|---|---|---|
| 1 | `t_kind1` (`var_58`) | `round(min_dim · 0.1750.15)` |
| 2 | `t_kind2` (`var_60`) | `round(min_dim · 0.2220.1)` |
| 3 | `t_kind3` (`var_50`),或在 `[topo+0x134]==1 && i==[topo+0x130]` 时 direct | `round(min_dim · 0.2070.2)` |
| *(default / kind-3 skip)* | 直接逐轴 | `round(axis_size[i] · 0.1450.3)` |
| *(axis-property == 2)* | 跳过(阈值保持 0||

> **NOTE —** `vc_balance_enabled` / `flag_0x14` gate 在**两个**地方执行,而不只是运行时的 `GetVcBalanceUsage`。`CreateVcBalanceThreshold` 携带**相同**的 gate(`BYTE[gen+9]==1 && !BYTE[gen+0x14]`,line 56):如果 balancing 被禁用,阈值 vector 会 resize,但保持全零,并且函数提前返回。零阈值会让 `GetVcBalanceUsage` 的 `hops ≤ threshold[orient-1]` 只在 `hops ≤ 0` 时为真(即对真实 hop 基本永远不会为真),因此运行时 gate 和构建时构造冗余地禁用 balancing,构造过程并不只依赖运行时 gate。
>
> **NOTE —** 三种轴 “kind” 与物理轴类别(wrap / twist / dateline)的绑定是从 `~0.2 · axis_size` 缩放和 kind 3 下的单轴 skip 推断出来的,并未锚定到具名 proto 字段。因此 kind → axis-class 映射为 MEDIUM confidence;*机制*(三个候选值由 `[topo+0xe8] ∈ {1,2,3}` 选择,并带 `[topo+0x130/0x134]` skip 对)已经字节确认。

---

## Multipod 跨 pod 路由发射

### 目的

`multipod::RoutingTableGenerator::Generate` @ `0x1fbf03a0`(单独的翻译单元,`dragonfish/multipod/routing_table_generator.cc`)构建逐 pod 表*之上*的路由表层。这里包含它,是因为它的 VC 处理是上文级联的有意对照:跨 pod 链路**没有环面环**,因此 multipod next-hop 表固定 VC,而不是运行 dateline/turn/balance discipline。multipod emitter 的模型、坐标映射和 DOR 距离规则记录在 [`GetStaticPath`](../routing/get-static-path.md#multipod-inter-pod-route-emission);本节只覆盖本页负责的 VC 和无死锁方面。

### 固定 VC 及其原因

```c
function SetNextHopRoutingTableEntry(this, src, dst, idx, table):   // 0x1fbf1a80
    if src == dst:                                                 // terminal entry (immediate dst)
        table.SetUnicastTerminal(idx, 1)                           // line 0x1fbf1ac0
        table.SetUnicastVcControl(idx, /*vc=*/1, true)             // FIXED VC=1, mov ecx,1 @0x1fbf1ada
    else:
        dir  = GetNextRoutingDirection(src, this_chip)             // 0x1fbf1aXX
        next = topo.GetCoordinate(dir)                             // vtable+0x90, line 163 (advance one chip)
        link = LinkMap.GetLink(this.link_map, dir)                 // 0x1fbf1de2
        // diagnostics only — the result of these is logged but does NOT pick the VC:
        crossed = CrossesDateline(src)                             // 0x1fbf28e0; logs "Crossed … high VC!" (:217)
        turned  = !Direction::IsSame(this_dir, next_dir)           // logs "Turned … low VC!" (:221)
        table.SetUnicastTarget(idx, link, 1)                       // line 0x1fbf1c??
        table.SetUnicastVcControl(idx, /*vc=*/1, true)             // FIXED VC=1, mov ecx,1 @0x1fbf1c69

function SetEgressRoutingTableEntry(this, src, dst, idx, table):   // 0x1fbf17c0
    if src == dst:  table.SetUnicastTerminal(idx, false)           // line 0x1fbf17fd
    else:
        dir  = GetNextRoutingDirection(src, dst)                   // line 0x1fbf184e
        link = LinkMap.GetLink(this.chip_id, dir)                  // line 0x1fbf1914
        table.SetUnicastTarget(idx, link, false)                   // line 0x1fbf1936
        // NO SetUnicastVcControl on the egress hop

QUIRK — multipod next-hop 表在每个 hop 上都写入固定 VC = 1,无论终止还是非终止。它不是 slice-builder dateline/turn/balance 级联,尽管非终止路径仍然会求值 dateline 和 turn 谓词(multipod::CrossesDateline @ 0x1fbf28e0Direction::IsSame),并发出相同的两个 VLOG 字符串:"Crossed a dateline, forced to high VC!" (:217) 和 "Turned, forced to low VC!" (:221)。这些谓词只驱动日志;传给 SetUnicastVcControl 的 VC 立即数无论结果如何都是硬编码的 1。跨 pod 链路是点到点光 hop,没有跨 pod 的环面通道循环需要打破,因此单个 VC 足够,dateline discipline 不需要在这里选择 VC。egress 表完全写入 SetUnicastVcControl(egress 是进入 next-hop 机制的本地 hop)。VC 立即数 1 已经字节确认:0x1fbf1ada(terminal)和 0x1fbf1c69(next-hop)处的 mov ecx, 1,每处都紧挨着后续的 SetUnicastVcControl 调用;IDA 伪代码折叠了该立即数,只打印 SetUnicastVcControl(table)

无死锁由下层代理

c
function GeneratesDeadlockFreeTables(this):               // 0x1fbf2e40
    return false                                           // xor eax,eax; ret
```text

`GeneratesDeadlockFreeTables` 是 `xor eax,eax; ret`,即 multipod 层****自行保证无死锁。pod 内无死锁来自逐 pod slice-builder VC 级联(本页的 [3 路规则](#3--vc-级联));跨 pod 无死锁来自无环的点到点光链路。`SetChannelMerges` @ `0x1fbf2100` 专门在 `LinkMap::GetHighLatencyLinks` (`0x1fbf220f`) 上应用 `AddChannelMerge` (`0x1fbf24c3`),即光链路 / 长跨 pod 链路;因此跨 pod fabric 的通道合并精确针对那些高延迟 hop。

### 函数映射

| 函数 | 地址 | 作用 |
|---|---|---|
| `multipod::SetNextHopRoutingTableEntry` | `0x1fbf1a80` | Next-hop writer + fixed VC=1 |
| `multipod::SetEgressRoutingTableEntry` | `0x1fbf17c0` | Egress writer(无 VcControl) |
| `multipod::SetChannelMerges` | `0x1fbf2100` | 在高延迟链路上执行 `AddChannelMerge` |
| `multipod::GeneratesDeadlockFreeTables` | `0x1fbf2e40` | 返回 `false`(委托给逐 pod) |
| `multipod::CreateDateline` | `0x1fbf0b20` | 构建 multipod dateline(仅 call site) |

> **NOTE —** multipod `CreateDateline` @ `0x1fbf0b20` 从 `Generate` (`0x1fbf0617`) 调用,但其函数体尚未按字节解码。考虑到跨 pod next-hop 使用固定 VC=1,multipod dateline 大概只用于逐 pod(pod 内回绕);跨 pod mesh hop 在构造上是无环的。标记为 LOW,直到函数体被解码。

---

## 相关 struct 偏移

| 偏移 (`gen`) | 字段 | 读取者 |
|---|---|---|
| `+0x9` | `vc_balance_enabled` (bool) | `GetVcBalanceUsage` (`0x1fbdb4ed`), `CreateVcBalanceThreshold` (line 56) |
| `+0x14` | gating flag / `use_limited_ici` (bool) | `GetVcBalanceUsage` (`0x1fbdb4f4`), `CreateVcBalanceThreshold`, `GetStaticPath` |
| `+0x60` / `+0x68` | `vc_balance_threshold` `vector<int>` base/end | `GetVcBalanceUsage` (`0x1fbdb55c`), `CreateVcBalanceThreshold` (`0x1fbd8746`) |
| `+0x20` | primary topology (twist / resilient) | distance + `Walk` + axis metadata |

| 偏移 (`topo`) | 字段 | 使用者 |
|---|---|---|
| `+0x30` | axis-property (`== 2` ⇒ skip balance) | `CreateVcBalanceThreshold` (line 178) |
| `+0x48` | per-axis size vector | `CrossesDateline` (`0x1fbdb246`) |
| `+0x50` | `GetDimensionSizes` / `num_dimensions` | `CreateVcBalanceThreshold` (line 58) |
| `+0x78` | dateline-applies bitmap | `CrossesDateline` (`0x1fbdb1be`) |
| `+0x90` | per-axis dateline coordinate | `CrossesDateline` (`0x1fbdb223`) |
| `+0xa0` | `Walk(Coord, Direction)` | `CrossesDateline`(路径遍历) |
| `+0xe8` | axis "kind" `∈ {1,2,3}` | `CreateVcBalanceThreshold` (`0x1fbd8770`) |
| `+0x130` / `+0x134` | single-axis skip pair (kind 3) | `CreateVcBalanceThreshold` (line 232) |

---

## 相关组件

| 组件 | 关系 |
|---|---|
| `GetStaticPath` | 产生静态路径,其 turn / dateline-cross / hop-count 形状会输入此规则 |
| `GetNextHopAction` | 承载级联;先解析 `output_link`,再调用此规则 |
| Unicast route emission | 安装级联填充的 `{output_link, vc, terminal}` `RoutingEntry` |
| `RandomizedToroidalWildFirstPaths` | 具备弹性的 fault-aware 路径生成器;共享同一 VC 级联和 dateline 谓词 |

## 交叉引用

- [GetStaticPath 与 Multipod](../routing/get-static-path.md) — 确定性路径生成器(torus-vs-mesh 选择 + `DirectionHops` 打包)以及完整 multipod 模型;本页覆盖那一页委托到这里的 VC 规则和无死锁性
- [路由表生成](../routing/route-table-generation.md) — `RouteTargetCache` 快路径,它在 miss 时调用 `GetStaticPath`,并把路由输入此级联
- [Unicast 路由发射](../routing/unicast-route-emission.md) — 安装此规则填充的 `{output_link, vc, terminal}` `RoutingEntry` 的 emitter
- [RandomizedToroidalWildFirstPaths](../routing/randomized-toroidal-wildfirst.md) — 具备弹性、规避故障的同级路径生成器,复用此 VC 级联
- [GetDistances](../routing/get-distances.md) — 扭曲环面距离度量,其回绕边定义 dateline 适用的位置
- [路由概览](../routing/overview.md) — 此规则所在的 route-generation → cache → emission pipeline
- [ICI 概览](overview.md) — ICI fabric 章节入口
- [拓扑发现](topology-discovery.md) — 拓扑对象(`gen+0x20`)、其 dateline bitmap 和轴元数据的来源
- [DMA 描述符](dma-descriptor.md) — 级联发出的 `output_link` / VC 的运行时消费者
- [故障恢复](failure-recovery.md) — 链路丢失时重新运行路由生成(从而重新运行此规则)的故障处理