Skip to content

单播路由发射

地址适用于 libtpu-0.0.40-cp314 wheel 中的 libtpu.so。其他版本会不同。二进制: extracted/libtpu-0.0.40-cp314-cp314-manylinux_2_31_x86_64/libtpu/libtpu.so(build-id 89edbbe81c5b328a958fe628a9f2207d,build libtpu_lts_20260413_b_RC00.text VMA == 文件偏移)。状态: 可重实现级 · 证据等级: 已确认(字节锚定)— ParallelRoutingTableGenerator::CreateUnicastRoutingTables@0x1fbd5340)、它的两个捕获 lambda(@0x1fbd70c0 / @0x1fbd7240)、CreateSrcDestUnicastRoutingTable@0x1fbd5640)以及串行孪生函数(@0x1fbd94a0 / @0x1fbd9580)均已与 IDA 反编译结果交叉核对。Part XII — Interconnect & Routing / Routing · 返回索引

摘要

本页记录单播路由表发射层:这个驱动把 slice 的拓扑转换为硅片要编程的逐芯片路由表。它位于逐 (src,dst) 路由表条目映射器(返回单个 next-hop 索引)上一层,位于 Generate() 之下一层。它的工作是遍历 slice 中每个 (src, dst) 芯片对,向路径生成器询问从 srcdst 的跳序列,并把每一跳的 {output_link, vc_control} 动作写入正确的 superpod::routing::RoutingTable 行,从而生成每个芯片为把 DMA 转发到任意目的地所需的密集转发状态。

这一层是一个二维遍历:源轴(外层)和目的轴(内层)。ParallelRoutingTableGenerator::CreateUnicastRoutingTables@0x1fbd5340)通过为每个源芯片生成一个 fiber 来并行化源轴;在每个 fiber 内部,目的循环串行运行,每个 (src, dst) 对调用一次 CreateSrcDestUnicastRoutingTable@0x1fbd5640)。基础 RoutingTableGenerator 发布了字节相同的串行孪生实现(@0x1fbd94a0 驱动 WalkCreateSrcDestUnicastRoutingTable @0x1fbd9580),仅去掉 fiber 包装。错误单位是每个源:每个 fiber 的 absl::Status 槽位捕获其目的循环中的第一个失败,驱动在 Join 后传播第一个 non-OK 槽位。

CreateSrcDestUnicastRoutingTable 是核心。对于一个 (src, dst),它通过拓扑接口解析两个芯片坐标,取得源的两张可写表(egress 和 next-hop),获得一个 IciRoutePath——当 use_cache_ 置位时来自预计算的 RouteTargetCache,否则由 GetStaticPath 实时计算——然后在多跳情况下写入源的第一跳 egress 链路,或在 src == dst 情况下标记目的终端。下游逐跳扇出(PopulateRoutingTableGetNextHopAction)会遍历剩余跳并写表;本页把该驱动消费和写入的表格式内部(GetTableIndex、三组 stride-0x48RoutingTable 数组、RouteTargetCache 二维路径数组)一并描述。

单个 (src,dst) 索引映射器(DmaDestinationRoutingTableEntryMapper::Map)和物理↔逻辑放置映射(GetPhysicalToLogicalMapping3D不归本页负责,见 Route-Table Generation。确定性的逐 pair 路径生成器(GetStaticPath)见 Static-Path Generation。本页从发射驱动以及位于条目映射器之上的构建循环开始。

对于重实现,契约是:

  • 二维遍历形态 — 源轴并行(每个源芯片一个 fiber),每个 fiber 内部的目的轴串行;把每个源的第一个错误捕获到预填 OK 的 vector<absl::Status> 中。
  • (src,dst) 条目构建 — 坐标解析 → 获取 egress + next-hop 表 → 路径来源分派(缓存 vs 实时)→ 第一跳 egress 写入或 terminal 写入 → 剩余跳扇出。
  • 它写入的表布局GetTableIndex chip→dense-row 映射、三组 RoutingTable 数组(egress / egress-next-hop / link-next-hop,stride 0x48)、RouteTargetCache [src_row][dst] 路径数组,以及 0xfe/0xff 哨兵。

一览

方面值(字节锚定)
并行驱动ParallelRoutingTableGenerator::CreateUnicastRoutingTables @0x1fbd5340
串行驱动(base)RoutingTableGenerator::CreateUnicastRoutingTables @0x1fbd94a0
外层 fiber lambda$_0 RemoteInvoker @0x1fbd70c0(每个源芯片一个任务)
内层 dst lambdaRemoteInvoker @0x1fbd7240(dst 循环 + SetChannelMerges
(src,dst) 构建CreateSrcDestUnicastRoutingTable @0x1fbd5640
串行逐 (src,dst) 构建WalkCreateSrcDestUnicastRoutingTable @0x1fbd9580
逐跳动作PopulateRoutingTable @0x1fbdb5c0GetNextHopAction @0x1fbda6a0
Chip→row 映射GetTableIndex @0x1fbdd000(crc32 swiss-table)
源 egress 表GetEgressTable @0x1fbdc040gen+0xa8,stride 0x48
Next-hop 表GetNextHopTable @0x1fbdbb00gen+0xc0 egress-next / gen+0xd8 link-next)
路径缓存RouteTargetCache::GetPath @0x1fbd42c0[src_row][dst]IciRoutePath stride 0x50
use_cache_ 标志gen+0x129*((_BYTE*)this+297)
numsrcs / chipcountgen+0x3c(Span size)/ gen+0x40topology->TotalSize()
RoutingTable settersSetUnicastTarget @0x1ffdfce0 · SetUnicastTerminal @0x1ffe0040 · SetUnicastVcControl @0x1ffe0320
Source TUparallel_routing_table_generator.cc@0x875bc20 = n_hop_routing_table_generator.cc family)

1. 这一层的位置

1.1 路由生成栈

Generate() 编排四个阶段;本页负责最后两个:

text
RoutingTableGeneratorFactory::CreateGenerator @0x1fbd3dc0   ── pick base / Parallel / NHop / multipod
  └─ Generate
       ├─ InitializeGenerator @0x1fbd7740                    ── topology@+0x20, LinkMap@+0x30,
       │                                                        TableIndex map@+0x108, 3 RoutingTable arrays
       ├─ [use_cache_] RouteTargetCache populate              ── PopulatePathCache + PopulateLinkNextCache
       ├─ CreateUnicastRoutingTables  ◄── THIS PAGE           ── the 2-D src×dst sweep
       │    └─ CreateSrcDestUnicastRoutingTable ◄── THIS PAGE ── one (src,dst) entry build
       │         └─ PopulateRoutingTable → GetNextHopAction   ── per-hop {output_link, next_chip, vc}
       └─ (table installed by SetRoutingTable on the runtime side)
```text

工厂在四个具体 generator 中选择;它们共享同一个 `Generate()` 骨架和相同的逐 `(src,dst)` 条目构建逻辑,只在**串行 vs fiber 化**遍历和 pod 作用域上不同:

| Generator | Vtable | `CreateUnicastRoutingTables` | 遍历 |
|-----------|--------|------------------------------|-------|
| `RoutingTableGenerator`(base) | `0x21f56fb0` | `@0x1fbd94a0` | 串行 — 普通 DOR |
| `ParallelRoutingTableGenerator` | `0x21f56f28` | `@0x1fbd5340` | 每个源一个 fiber |
| `viperlite_pod::NHopRoutingTableGenerator` | `0x21f57cc8` | (n-hop) | n-hop 端口表 |
| `multipod::RoutingTableGenerator` | `0x21f57c40` | (multipod) | pod 间 |

> **说明 —** base 和 Parallel generator 构建**相同的表**;Parallel 版本只是在源轴上增加 fiber 层。重实现可以先发布串行驱动,再加并行化,而无需改变条目构建逻辑。`CreateGenerator @0x1fbd3dc0` 根据由 `FLAGS_tpu_slice_builder_ici_route_force_n_hop`(`@0x22479fb8`)门控的 routing-strategy enum 分派。

### 1.2 这一层读取的 generator 状态

驱动和条目构建会读取一组固定的 generator 字段。下面是重实现必须在发射运行前由 `InitializeGenerator` 填好的输入:

| 字段 | 偏移 | 含义 |
|-------|--------|---------|
| topology | `gen+0x20`(`*((qword*)this+4)`) | 抽象 `ToroidalTopologyInterface*`;经 vtable 分派 |
| LinkMap | `gen+0x30`(`*((qword*)this+6)`) | `(chip, Direction) → output_link` 解析器 |
| numsrcs | `gen+0x3c`(`*((int*)this+15)`) | source-Span 大小;status-vector 长度 |
| chipcount | `gen+0x40`(`*((int*)this+16)`) | `topology->TotalSize()`;内层 dst-loop 上界 |
| compacted | `gen+0x18` | next-hop-table 压缩标志 |
| nexthop-enable | `gen+0x1a` | 是否存在 next-hop 表 |
| egress array | `gen+0xa8` / count `gen+0xb0` | 每源第一跳 `RoutingTable[]`,stride `0x48` |
| egress-next-hop | `gen+0xc0` / count `gen+0xc8` | 每芯片转发 `RoutingTable[]`,stride `0x48` |
| link-next-hop | `gen+0xd8` / count `gen+0xe0` | 每 `(chip,in-link)` `RoutingTable[]`,stride `0x48` |
| TableIndex map | `gen+0x108`/`+0x118`/`+0x120` | crc32 swiss-table chip_id → dense row |
| `use_cache_` | `gen+0x129` | 缓存 `GetPath` vs 实时 `GetStaticPath` |
| RouteTargetCache | `gen+0x130`(`*((qword*)this+38)`) | 二维路径 + 每链路字节缓存 |

> 上述每个偏移都作为直接字段访问出现在 `CreateUnicastRoutingTables`/`CreateSrcDestUnicastRoutingTable` 中:`gen+0x3c` 为 `*(int*)(a1+60)`(status-vector 大小,`@0x1fbd5343`),`gen+0x40` 为 `*((int*)v2+16)`(内层循环上界,`@0x1fbd7240`),`use_cache_` 为 `*((_BYTE*)this+297)`(`cmpb $1,0x129(%r15)` `@0x1fbd573d`),`RouteTargetCache` 位于 `*((qword*)this+38)`(`gen+0x130`)。`use_cache_` 字段名由一致性断言 `LogMessageFatal(..., 417, "use_cache_")` 确认。

---

## 2. 二维遍历 — `CreateUnicastRoutingTables`

### 2.1 目的

通过遍历完整的 `src × dst` 网格来构建每个芯片的路由表。目的循环是正确性的核心;源循环是并行轴。Parallel 变体为每个源生成一个 fiber,使独立的逐源表可并发填充,然后把每个 fiber 的状态折叠为一个结果。

### 2.2 入口点

```text
ParallelRoutingTableGenerator::CreateUnicastRoutingTables @0x1fbd5340
  ├─ alloc vector<absl::Status> result[gen+0x3c]   ── pre-filled OK (discriminant 1)
  ├─ thread::Fiber (352 B)                          ── runs the $_0 outer invoker
  │    └─ $_0 outer lambda @0x1fbd70c0              ── one Bundle task per source chip
  │         └─ inner dst lambda @0x1fbd7240         ── dst loop + SetChannelMerges
  │              └─ CreateSrcDestUnicastRoutingTable(src,dst)
  ├─ Fiber::Start / Fiber::Join
  └─ scan result[] → first non-OK, else OK

2.3 算法

c
function CreateUnicastRoutingTables(gen, Span<int> srcs):   // @0x1fbd5340
    n = gen[+0x3c]                                  // numsrcs (= Span size)
    result = new absl::Status[n]                    // operator new(8*n)
    fill(result, OK)                                // each slot = 1 (ok discriminant)
                                                    // vmovddup qword_A2DF228 + tail *q=1 loop
    fiber = new thread::Fiber(352)                  // operator new(352, 16)
    bind fiber -> RemoteInvoker<$_0>{ gen, &srcs, &result }   // @0x1fbd70c0
    fiber.Start()
    fiber.Join()
    for i in [0, n):                                // first-error scan
        if result[i] != OK: return result[i]
    return OK

// outer lambda — one Bundle task per source chip            // @0x1fbd70c0
function Outer(gen, srcs, result):
    bundle = thread::Bundle()
    for src in srcs:                                // Span step +4
        row = GetTableIndex(src).value              // dense row index
        bundle.AddImpl( Inner{ gen, src, &result[row] } )   // one fiber per source
    bundle.JoinAll()

// inner lambda — the destination loop                       // @0x1fbd7240
function Inner(gen, src, result_slot):
    chipcount = gen[+0x40]                          // *((int*)gen+16)
    for dst in [0, chipcount):
        st = CreateSrcDestUnicastRoutingTable(gen, src, dst)
        if *result_slot == OK and st != OK:         // first-error capture
            *result_slot = st
        else if st is error: Unref(st)
    st = SetChannelMerges(gen, src)                 // @0x1fbda1e0 — merge per-source VC/channel
    if *result_slot == OK and st != OK: *result_slot = st
```text

> status vector 是 `operator new(8*v5)`,其中 `v5 = *(int*)(a1+60)` = `gen+0x3c`,由 `vmovddup cs:qword_A2DF228` 块和 `*(_QWORD*)v9 = 1` 尾循环(`@0x1fbd5400`)预填为 OK。`thread::Fiber` 是 `operator new(352, 16)`,绑定到 `RemoteInvoker<...CreateUnicastRoutingTables...$_0&&>`,捕获 `{gen, &srcs, &result}`(`@0x1fbd7240` 调用方处的 24 字节 `operator new(0x18)` closure),然后 `Start`/`Join`。Join 后扫描会遍历 `result[i]` 查找第一个 `!= OK` 的槽位(`@0x1fbd5570`)。
>
> 内层 lambda(`@0x1fbd7240`)循环 `dst = 0 .. *((int*)v2+16)-1`(chipcount = `gen+0x40`),调用 `CreateSrcDestUnicastRoutingTable(gen, src, dst)`,并应用 first-error 规则 `if (*result_slot == 1 && st != 1) *result_slot = st;`(反编译第 23-32 行)。循环后它用相同的 first-error 捕获调用 `SetChannelMerges(gen, src)`(第 38 行)。外层 lambda(`@0x1fbd70c0`)打开一个 `thread::Bundle`,通过 `GetTableIndex` 解析每个源,并在 `JoinAll` 前为每个源提交一个 `Bundle::AddImpl` 任务。
>
> **特性 —** 错误模型是**每个源,第一个错误胜出**。每个 fiber 正好拥有一个 `result[]` 槽位(按 `GetTableIndex(src)` 而不是原始芯片 ID 键控),后续 dst 失败无法覆盖更早的失败。记录*最后*一个错误,或在多个源之间共享一个状态的重实现,会与二进制诊断不同。

### 2.4 串行孪生实现

base generator 的 `CreateUnicastRoutingTables @0x1fbd94a0` 是没有 fiber 的同一遍历:

```c
function CreateUnicastRoutingTables_serial(gen, Span<int> srcs):   // @0x1fbd94a0
    for src in srcs:
        for dst in [0, gen[+0x40]):
            WalkCreateSrcDestUnicastRoutingTable(gen, src, dst)     // @0x1fbd9580
        SetChannelMerges(gen, src)

@0x1fbd94a0 包含嵌套的 for src / for dst<[gen+0x40] 循环,调用 WalkCreateSrcDestUnicastRoutingTable@0x1fbd94f9),随后调用 SetChannelMerges@0x1fbd951a)。WalkCreateSrcDestUnicastRoutingTable @0x1fbd9580 镜像并行条目构建(GetCoordinate、GetStaticPath、egress gen+0xa8、HopDirection、SetUnicastTerminal),但硬连到实时 GetStaticPath 路径源;它是 §3 的非缓存对应实现。


3. 逐 (src,dst) 条目构建 — CreateSrcDestUnicastRoutingTable

3.1 目的

对于单个 (src, dst) pair,生成让从 src 发起的 DMA 能到达 dst 的路由表条目:源的第一跳 egress 条目,以及路径上中间转发条目链(或当 src == dst 时的单个 terminal 条目)。它负责路径源分派和第一跳;剩余跳委托给 PopulateRoutingTable

3.2 签名

cpp
// @0x1fbd5640  (parallel_routing_table_generator.cc)
absl::Status
ParallelRoutingTableGenerator::CreateSrcDestUnicastRoutingTable(int src /*a2*/, int dst /*a3*/);
```text

返回值是裸 `absl::Status`(不是条目映射器的 `StatusOr<int>`);所有副作用都落在 generator 的 `RoutingTable` 数组中。

### 3.3 算法

```c
function CreateSrcDestUnicastRoutingTable(gen, int src, int dst):    // @0x1fbd5640
    // 1. resolve both chip coordinates (topology vtable slot +0x88)
    src_coord = topology->GetCoordinate(src)     // @0x1fbd566d, line 371 src-loc
    dst_coord = topology->GetCoordinate(dst)     // @0x1fbd56a7, line 373
        // either StatusOr error short-circuits to the cleanup chain

    // 2. fetch the source's two writable tables
    egress  = GetEgressTable(src)                // @0x1fbd56db, line 375 -> &egress[TableIndex(src)]
    nexthop = GetNextHopTable(src, /*egress=*/true)   // @0x1fbd5701, line 377

    // 3. PATH SOURCE dispatch on use_cache_ (gen+0x129)
    if gen[+0x129] == 1:                         // @0x1fbd573d  (cmpb $1,0x129(%r15))
        path = RouteTargetCache::GetPath(src, dst)        // @0x1fbd42c0 — precomputed IciRoutePath
    else:
        path = GetStaticPath(src_coord, dst_coord)        // @0x1fbd57f6, line 384 — compute now

    // 4. emit
    if path.num_hops > 0:                        // *(int*)(path+0x20) > 0 ; @0x1fbd5742 / line 388
        dir0 = path.HopDirection(0)              // @0x20c01900, line 401
        link = LinkMap::GetLink(src, dir0)       // @0x1ffe3940, line 403 — source's SerDes egress
        if egress.SetUnicastTarget(dst, link, /*overwrite=*/false) == OK:   // @0x1ffdfce0, line 405
            PopulateRoutingTable(src_coord, dst_coord, src, dst, path, /*hop=*/0)  // @0x1fbd5ad0, line 410
            if gen[+0x1a] (nexthop-enable):
                // cached fast path mirrors the first link-next byte into the nexthop table
                assert use_cache_                // LogMessageFatal(..., 417, "use_cache_")
                b = cache.link_next_byte[ src*numdst + dst ]    // [gen+0x130]+0x48, line 428
                if b != 0xfe:                    // unset sentinel -> skip
                    if b == 0xff: nexthop.SetUnicastTerminal(dst, false)   // line 431
                    else:         nexthop.SetUnicastTarget(dst, b, false)   // line 434
                    nexthop.SetUnicastVcControl(dst, /*vc*/, true)         // line 443
    else:                                        // src == dst — local delivery, no hops
        egress.SetUnicastTerminal(dst, false)    // @0x1ffe0040, line 391
        nexthop.SetUnicastTerminal(dst, false)   //              line 394
        nexthop.SetUnicastVcControl(dst, 1, true) // @0x1ffe0320, line 396
    return OK

两次坐标获取都使用拓扑 vtable 槽位 +136+0x88(**((qword**)this+4) + 136)),分别在 @0x1fbd566d(src,AddSourceLocation 行 371)和第二次的行 373。随后执行 GetEgressTable(行 375)和 GetNextHopTable(src, …)(行 377)。路径源分支测试 *((_BYTE*)this + 297)gen+0x129,反编译第 120 行):true → RouteTargetCache::GetPath(cache, src, dst);false → GetStaticPath(this, src_coord)。跳数保护读取 *(_DWORD*)v51(路径的 num_hops);多跳分支调用 HopDirectionLinkMap::GetLinkSetUnicastTarget,然后 PopulateRoutingTable;无跳分支(LABEL_17)调用两次 SetUnicastTerminal + SetUnicastVcControl

注意 — 第一跳是源自身的 egress,直接在这里写入,而不是由 PopulateRoutingTable 写入。随后以 hop = 0 调用 PopulateRoutingTable,用于遍历路径并填充中间芯片的转发表。让逐跳扇出也写源 egress 的重实现会双写 egress 行(SetUnicastTargetoverwrite=false 参数会让第二次写入成为 no-op,但两条路径的 link-byte 计算不同)。

特性 — 空路径情况恰好是 src == dstGetStaticPath/GetPath 返回 num_hops == 0 的路径,条目构建会在 egress 和 next-hop 表中都写入 terminal 标记,使到达自身目的地的包被本地交付而不是继续转发。terminal 条目上的 SetUnicastVcControl(dst, 1, true) 分配默认 VC。

3.4 路径源分派

use_cache_ 标志(gen+0x129)在两个返回相同形态的 IciRoutePath 提供者之间选择:

use_cache_提供者成本来源
1RouteTargetCache::GetPath(src, dst) @0x1fbd42c0O(1) 数组索引PopulatePathCache 中预计算
0GetStaticPath(src_coord, dst_coord) @0x1fbdbd00现在计算实时 DOR/twist 构造

GetPath 索引一个二维数组:idx = GetTableIndex(src) · numdst + dstIciRoutePath stride 0x50lea rax,[rax+rax*4]; shl 4),基指针位于 cache+0x0numdst 通过 cache+0x48 字段间接读取(*(int*)([cache+0x48]+0x40))。实时生成器内部见 Static-Path Generation

RouteTargetCache::GetPath @0x1fbd42c0 计算 idx = TableIndex(src)·numdst + dst,其中 numdst = *(_DWORD*)([cache+0x48]+0x40)@0x1fbd42f4 处的 mov 0x48(%rdi),%rsi; imul 0x40(%rsi),%edx),并返回 *(qword*)cache + 0x50·idx@0x1fbd4304 处的 lea (rax,rax,4); shl 4)。目的数量通过 cache+0x48 指针间接存放一层,不是直接在 cache+0x40cache+0x8 字段是用于边界检查的路径数组元素数。GetTableIndex(src) 只应用于非 fast-path 分支(当 *(_BYTE*)([cache+0x48]+24) == 0 时)。


4. 逐跳动作 — PopulateRoutingTableGetNextHopAction

4.1 目的

给定路径和跳索引,计算该跳的 {next_chip, output_link, vc} 动作,并写入正确的转发 RoutingTable 行。PopulateRoutingTable 是选择表的包装层;GetNextHopAction 是拓扑解码器。

4.2 GetNextHopAction

c
function GetNextHopAction(src_coord, dst_coord, IciRoutePath& path, int hop):  // @0x1fbda6a0
    dir        = path.HopDirection(hop)              // @0x20c01900 — proto Direction of this hop
    next_coord = topology->Walk(src_coord, dir)      // vtable +0xa0, @0x1fbda702
    next_chip  = topology->GetId(next_coord)         // vtable +0x90, @0x1fbda74b
    if next_coord == dst_coord:                      // Coordinates::operator== @0x20c0bac0
        if hop == path.num_hops - 1:                 // [path+0x20]-1
            output_link = 0xff                       // TERMINAL — next chip IS the destination
        else: ...                                    // (defensive; reached-dst-but-more-hops)
    else:                                            // intermediate — next chip must forward on
        next_dir = path.RemainDirectionHops(hop)     // @0x20c01ba0 — its outgoing direction
        output_link = LinkMap::GetLink(next_chip, next_dir)   // @0x1ffe3940 — its egress link byte
    // VC selection — deadlock-free torus VC allocation (rule not fully reduced)
    vc = vc_select( CrossesDateline(src,dst),        // @0x1fbdb120
                    Direction::IsSame(dir,next_dir),  // @0x20c025e0
                    GetVcBalanceUsage() )             // @0x1fbdb4c0  -> vc in {0,1,2}
    return { next_chip @+8, output_link @+0xc, vc @+0x10 }
```text

> `GetNextHopAction @0x1fbda6a0`:`HopDirection`(`@0x1fbda6d4`)、经 vtable `+0xa0` 的 `Walk`(`@0x1fbda702`)、经 vtable `+0x90` 的 `GetId`(`@0x1fbda74b`)、与 `dst_coord` 比较的 `Coordinates::operator==`(`@0x1fbda773`)、last-hop terminal `0xff`(`@0x1fbda79c`)、`RemainDirectionHops(hop)`(原始 hop 索引,`@0x1fbda7bb`)、`LinkMap::GetLink`(`@0x1fbda863`)。VC 输入 `CrossesDateline`(`@0x1fbda8b5`)、`Direction::IsSame`(`@0x1fbda8fd`)、`GetVcBalanceUsage`(`@0x1fbda921`)产生 `vc ∈ {0,1,2}`。结果结构打包 `{next_chip(int32)@+8, output_link(int8)@+0xc, vc(int32)@+0x10}`(`@0x1fbdaa6a`)。

### 4.3 `PopulateRoutingTable`

```c
function PopulateRoutingTable(src_coord, dst_coord, src_chip, via_chip, path, hop):  // @0x1fbdb5c0
    act = GetNextHopAction(src_coord, dst_coord, path, hop)
    if gen[+0x1a] (nexthop-enable):
        idx = GetTableIndex(via_chip)
        table = gen[+0xc0 egress-next | +0xd8 link-next][idx]   // by egress bool
    else:                                                       // egress branch
        in_link = LinkMap::GetLink(via_chip, Direction::Opposite(hopdir))  // @0x20c02600 / @0x1ffe3940
        table   = GetLinkHopTable(via_chip, in_link)            // @0x1fbdbbe0
    entry = table.GetRoutingEntry(dst)                          // @0x1ffdf740
    if act.output_link == 0xff: entry.SetUnicastTerminal(dst, false)
    elif act.output_link != 0xfe: entry.SetUnicastTarget(dst, act.output_link, false)
    entry.SetUnicastVcControl(dst, act.vc, true)
    return (act.output_link != 0xfe)                            // "wrote a target"

PopulateRoutingTable @0x1fbdb5c0GetNextHopAction@0x1fbdb5f6)、由 gen+0x1a 门控的 next-hop 表选择 gen+0xc0/+0xd8@0x1fbdb659)、经 Direction::Opposite@0x1fbdb6fb)+ LinkMap::GetLink@0x1fbdb773)+ GetLinkHopTable@0x1fbdb797)的 egress 分支、GetRoutingEntry@0x1fbdb85c)、SetUnicastTarget/SetUnicastTerminal/SetUnicastVcControl@0x1fbdb889/@0x1fbdb8b4/@0x1fbdb8cd),以及 bl = (link != 0xfe) 返回(@0x1fbdb876)。

说明 — 来自 {CrossesDateline, Direction::IsSame, GetVcBalanceUsage} 的 VC 分配规则是一个三级优先级级联,已由 GetNextHopActionr12d 立即数字节确认($_2/"Turned" 位置 0x1fbda9d1mov $0x1$_3/"Crossed a dateline" 位置 0x1fbda950$_4/"VC load balancing" 位置 0x1fbda9fdmov $0x2):一次转弯!IsSame)强制 VC1;穿过日期线的直行跳强制 VC2;触发均衡的直行跳强制 VC2;普通直行跳保持默认 VC0。因此 VC2( VC)是破死锁 / 均衡 VC,VC1 是转弯 VC——完整级联和 CreateVcBalanceThreshold @0x1fbd8320 阈值数学见 VC-Balance Allocation


5. 这一层写入的表布局

5.1 GetTableIndex — chip → dense row

GetTableIndex(chip_id) @0x1fbdd000 把(可能稀疏的)物理芯片 ID 映射为密集的 0 .. numsrcs-1 行索引,用于寻址每个逐源表。它是一个以 crc32 播种的 absl swiss-table(gen+0x108 size mask、gen+0x118 ctrl bytes、gen+0x120 slot array),并对小于 0x20000 芯片的 slice 提供线性 fast path。这种压缩意味着重实现不能用原始芯片 ID 索引表。

GetTableIndex @0x1fbdd000:chip 的 crc32(@0x1fbdd05c)、swiss-table 字段 gen+0x108/+0x118/+0x120< 0x20000 fast path(@0x1fbdd012)。

5.2 三组 RoutingTable 数组

发射会写三组并行的 superpod::routing::RoutingTable 数组,每行 stride 0x48

数组基址 / 数量角色索引方式
egressgen+0xa8 / gen+0xb0源的第一跳输出链路TableIndex(src)
egress-next-hopgen+0xc0 / gen+0xc8每芯片转发(已压缩)TableIndex(via_chip)
link-next-hopgen+0xd8 / gen+0xe0(chip, in-link) 转发TableIndex(via_chip) + in-link

GetEgressTable @0x1fbdc040 返回 &(gen+0xa8)[TableIndex(src)]GetNextHopTable @0x1fbdbb00 选择 egress-next(gen+0xc0egress=true)或 link-next(gen+0xd8egress=false),由 gen+0x1a 门控;GetLinkHopTable @0x1fbdbbe0 是非压缩 egress 分支的每 (chip, incoming-link) 表。每一行都是一个 RoutingTable,其 RoutingEntry 按目的索引,并携带 {unicast_target (output_link), unicast_terminal, vc_control}

5.3 RouteTargetCache

use_cache_ 时,RouteTargetCache(位于 gen+0x130)保存一个二维路径数组 [dense_src_row][dst]IciRoutePath stride 0x50,目的数量位于 cache+0x40)以及并行的每链路 next-hop 字节表,供 cached fast path 读取。PopulatePathCache @0x1fbd4360 填充路径数组(每个源一个 fiber,每个 fiber 运行 GetStaticPath);PopulateLinkNextCache @0x1fbd4680 填充 CreateSrcDest 行 428 / PopulateRoutingTable 读取的字节表。

5.4 条目哨兵

output_link 字节Setter含义
0..NSetUnicastTarget(dst, link, false) @0x1ffdfce0本跳的物理 SerDes 输出链路
0xfe(skip)条目未设置哨兵 — 如果已设置则 SetUnicastTarget no-op(@0x1ffdfd1e
0xffSetUnicastTerminal(dst, false) @0x1ffe0040terminal — 该芯片就是目的地
SetUnicastVcControl(dst, vc, true) @0x1ffe0320vc ∈ {0,1,2} 死锁/均衡控制

0xfe skip-if-set 位于 SetUnicastTarget @0x1ffdfce0@0x1ffdfd1e);0xff 是由 GetNextHopAction 的 last-hop 分支发出并路由到 SetUnicastTerminal 的 terminal 标记。这些 setter 是 superpod::routing::RoutingTablePerLinksRoutingTable 写入器,其 output_link 字节是运行时 LinkNextHopRoutingTablesEntry0..3 SerDes 链路索引。

注意 — 0xfe0xff同一字节字段中的不同哨兵0xfe 表示“尚未写入,保持不变”(幂等填充),0xff 表示“在此交付”。使用单个带外值,或把 0xff 当作另一个链路索引的重实现,会误路由未设置行或目的行。


6. 函数映射

函数地址角色
ParallelRoutingTableGenerator::CreateUnicastRoutingTables0x1fbd5340fiber 化二维遍历驱动
RoutingTableGenerator::CreateUnicastRoutingTables0x1fbd94a0串行遍历孪生实现
$_0 outer fiber lambda0x1fbd70c0每个源一个 Bundle 任务
inner dst lambda0x1fbd7240dst 循环 + SetChannelMerges + first-error
ParallelRoutingTableGenerator::CreateSrcDestUnicastRoutingTable0x1fbd5640(src,dst) 条目构建
RoutingTableGenerator::WalkCreateSrcDestUnicastRoutingTable0x1fbd9580串行逐 (src,dst) 孪生实现(仅实时路径)
RoutingTableGenerator::PopulateRoutingTable0x1fbdb5c0逐跳表选择 + 写入
RoutingTableGenerator::GetNextHopAction0x1fbda6a0hop → {next_chip, output_link, vc}
GetTableIndex0x1fbdd000chip_id → dense row(crc32 swiss)
GetEgressTable0x1fbdc040gen+0xa8[row],stride 0x48
GetNextHopTable0x1fbdbb00egress-next / link-next 选择
GetLinkHopTable0x1fbdbbe0(chip, in-link)
RouteTargetCache::GetPath0x1fbd42c0[src_row][dst] 路径索引
SetChannelMerges0x1fbda1e0每源 VC/channel 合并
GetVcBalanceUsage0x1fbdb4c0VC 均衡计数器(规则未完全化简)
RoutingTableGeneratorFactory::CreateGenerator0x1fbd3dc0strategy → generator class

7. 诊断源位置

全部通过 AddSourceLocationImpl / CreateStatusAndConditionallyLog 针对 parallel_routing_table_generator.cc 发出:

位置
371 / 373GetCoordinate(src) / GetCoordinate(dst) 错误
375 / 377GetEgressTable / GetNextHopTable 错误
384GetStaticPath 错误(实时路径)
388路径 num_hops 测试
391 / 394 / 396terminal 分支:egress / nexthop SetUnicastTerminalSetUnicastVcControl
401 / 403 / 405多跳分支:HopDirection(0)LinkMap::GetLinkSetUnicastTarget
410PopulateRoutingTable 错误
417use_cache_ 一致性断言(LogMessageFatal
428 / 431 / 434 / 443cached nexthop mirror:表获取、terminal、target、VcControl

上述每一行都是从 @0x1fbd5640 反编译中相应调用点读取的字面 AddSourceLocationImpl(..., N, "...parallel_routing_table_generator.cc") / CreateStatusAndConditionallyLog(N, ...) 参数。行 417 的 LogMessageFatal(..., 417, "use_cache_") 命名了该字段。


交叉引用

  • 路由概览 — route-generation → cache → emission 流水线,本驱动是其中的发射阶段。
  • 路由表生成 — 逐 (src,dst) 条目映射器(DmaDestinationRoutingTableEntryMapper::Map)和本层之下的物理↔逻辑放置映射(GetPhysicalToLogicalMapping3D);单索引原语 vs 完整表遍历。
  • 静态路径生成 — 非缓存发射按每个 (src,dst) 消费的确定性 GetStaticPath 提供者。
  • 创建路由调度 — 显式调度(CollectivePermute)路径,与此自动路由发射正交。
  • Net-Router 流水线 — 本层写入的 {next_chip, output_link, vc} 每链路行的下游消费者。
  • Collectives 概览 — replica group(通过 GetPhysicalToLogicalMapping3D 放置)如何驱动片上 collectives,其 DMA 会穿过这些表。