单播路由发射
地址适用于 libtpu-0.0.40-cp314 wheel 中的 libtpu.so。其他版本会不同。二进制:
extracted/libtpu-0.0.40-cp314-cp314-manylinux_2_31_x86_64/libtpu/libtpu.so(build-id89edbbe81c5b328a958fe628a9f2207d,buildlibtpu_lts_20260413_b_RC00;.textVMA == 文件偏移)。状态: 可重实现级 · 证据等级: 已确认(字节锚定)—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) 芯片对,向路径生成器询问从 src 到 dst 的跳序列,并把每一跳的 {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 情况下标记目的终端。下游逐跳扇出(PopulateRoutingTable → GetNextHopAction)会遍历剩余跳并写表;本页把该驱动消费和写入的表格式内部(GetTableIndex、三组 stride-0x48 的 RoutingTable 数组、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 写入 → 剩余跳扇出。 - 它写入的表布局 —
GetTableIndexchip→dense-row 映射、三组 RoutingTable 数组(egress / egress-next-hop / link-next-hop,stride0x48)、RouteTargetCache[src_row][dst]路径数组,以及0xfe/0xff哨兵。
一览
| 方面 | 值(字节锚定) |
|---|---|
| 并行驱动 | ParallelRoutingTableGenerator::CreateUnicastRoutingTables @0x1fbd5340 |
| 串行驱动(base) | RoutingTableGenerator::CreateUnicastRoutingTables @0x1fbd94a0 |
| 外层 fiber lambda | $_0 RemoteInvoker @0x1fbd70c0(每个源芯片一个任务) |
| 内层 dst lambda | RemoteInvoker @0x1fbd7240(dst 循环 + SetChannelMerges) |
逐 (src,dst) 构建 | CreateSrcDestUnicastRoutingTable @0x1fbd5640 |
串行逐 (src,dst) 构建 | WalkCreateSrcDestUnicastRoutingTable @0x1fbd9580 |
| 逐跳动作 | PopulateRoutingTable @0x1fbdb5c0 → GetNextHopAction @0x1fbda6a0 |
| Chip→row 映射 | GetTableIndex @0x1fbdd000(crc32 swiss-table) |
| 源 egress 表 | GetEgressTable @0x1fbdc040(gen+0xa8,stride 0x48) |
| Next-hop 表 | GetNextHopTable @0x1fbdbb00(gen+0xc0 egress-next / gen+0xd8 link-next) |
| 路径缓存 | RouteTargetCache::GetPath @0x1fbd42c0([src_row][dst],IciRoutePath stride 0x50) |
use_cache_ 标志 | gen+0x129(*((_BYTE*)this+297)) |
| numsrcs / chipcount | gen+0x3c(Span size)/ gen+0x40(topology->TotalSize()) |
| RoutingTable setters | SetUnicastTarget @0x1ffdfce0 · SetUnicastTerminal @0x1ffe0040 · SetUnicastVcControl @0x1ffe0320 |
| Source TU | parallel_routing_table_generator.cc(@0x875bc20 = n_hop_routing_table_generator.cc family) |
1. 这一层的位置
1.1 路由生成栈
Generate() 编排四个阶段;本页负责最后两个:
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 OK2.3 算法
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、egressgen+0xa8、HopDirection、SetUnicastTerminal),但硬连到实时GetStaticPath路径源;它是 §3 的非缓存对应实现。
3. 逐 (src,dst) 条目构建 — CreateSrcDestUnicastRoutingTable
3.1 目的
对于单个 (src, dst) pair,生成让从 src 发起的 DMA 能到达 dst 的路由表条目:源的第一跳 egress 条目,以及路径上中间转发条目链(或当 src == dst 时的单个 terminal 条目)。它负责路径源分派和第一跳;剩余跳委托给 PopulateRoutingTable。
3.2 签名
// @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);多跳分支调用HopDirection、LinkMap::GetLink、SetUnicastTarget,然后PopulateRoutingTable;无跳分支(LABEL_17)调用两次SetUnicastTerminal+SetUnicastVcControl。注意 — 第一跳是源自身的 egress,直接在这里写入,而不是由
PopulateRoutingTable写入。随后以hop = 0调用PopulateRoutingTable,用于遍历路径并填充中间芯片的转发表。让逐跳扇出也写源 egress 的重实现会双写 egress 行(SetUnicastTarget的overwrite=false参数会让第二次写入成为 no-op,但两条路径的 link-byte 计算不同)。特性 — 空路径情况恰好是
src == dst。GetStaticPath/GetPath返回num_hops == 0的路径,条目构建会在 egress 和 next-hop 表中都写入 terminal 标记,使到达自身目的地的包被本地交付而不是继续转发。terminal 条目上的SetUnicastVcControl(dst, 1, true)分配默认 VC。
3.4 路径源分派
use_cache_ 标志(gen+0x129)在两个返回相同形态的 IciRoutePath 提供者之间选择:
use_cache_ | 提供者 | 成本 | 来源 |
|---|---|---|---|
1 | RouteTargetCache::GetPath(src, dst) @0x1fbd42c0 | O(1) 数组索引 | 在 PopulatePathCache 中预计算 |
0 | GetStaticPath(src_coord, dst_coord) @0x1fbdbd00 | 现在计算 | 实时 DOR/twist 构造 |
GetPath 索引一个二维数组:idx = GetTableIndex(src) · numdst + dst,IciRoutePath stride 0x50(lea rax,[rax+rax*4]; shl 4),基指针位于 cache+0x0,numdst 通过 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+0x40;cache+0x8字段是用于边界检查的路径数组元素数。GetTableIndex(src)只应用于非 fast-path 分支(当*(_BYTE*)([cache+0x48]+24) == 0时)。
4. 逐跳动作 — PopulateRoutingTable → GetNextHopAction
4.1 目的
给定路径和跳索引,计算该跳的 {next_chip, output_link, vc} 动作,并写入正确的转发 RoutingTable 行。PopulateRoutingTable 是选择表的包装层;GetNextHopAction 是拓扑解码器。
4.2 GetNextHopAction
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 @0x1fbdb5c0:GetNextHopAction(@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 分配规则是一个三级优先级级联,已由GetNextHopAction的r12d立即数字节确认($_2/"Turned" 位置0x1fbda9d1的mov $0x1,$_3/"Crossed a dateline" 位置0x1fbda950和$_4/"VC load balancing" 位置0x1fbda9fd的mov $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、< 0x20000fast path(@0x1fbdd012)。
5.2 三组 RoutingTable 数组
发射会写三组并行的 superpod::routing::RoutingTable 数组,每行 stride 0x48:
| 数组 | 基址 / 数量 | 角色 | 索引方式 |
|---|---|---|---|
| egress | gen+0xa8 / gen+0xb0 | 源的第一跳输出链路 | TableIndex(src) |
| egress-next-hop | gen+0xc0 / gen+0xc8 | 每芯片转发(已压缩) | TableIndex(via_chip) |
| link-next-hop | gen+0xd8 / gen+0xe0 | 每 (chip, in-link) 转发 | TableIndex(via_chip) + in-link |
GetEgressTable @0x1fbdc040 返回 &(gen+0xa8)[TableIndex(src)];GetNextHopTable @0x1fbdbb00 选择 egress-next(gen+0xc0,egress=true)或 link-next(gen+0xd8,egress=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..N | SetUnicastTarget(dst, link, false) @0x1ffdfce0 | 本跳的物理 SerDes 输出链路 |
0xfe | (skip) | 条目未设置哨兵 — 如果已设置则 SetUnicastTarget no-op(@0x1ffdfd1e) |
0xff | SetUnicastTerminal(dst, false) @0x1ffe0040 | terminal — 该芯片就是目的地 |
| — | SetUnicastVcControl(dst, vc, true) @0x1ffe0320 | vc ∈ {0,1,2} 死锁/均衡控制 |
0xfeskip-if-set 位于SetUnicastTarget @0x1ffdfce0(@0x1ffdfd1e);0xff是由GetNextHopAction的 last-hop 分支发出并路由到SetUnicastTerminal的 terminal 标记。这些 setter 是superpod::routing::RoutingTable的PerLinksRoutingTable写入器,其output_link字节是运行时LinkNextHopRoutingTablesEntry的0..3SerDes 链路索引。注意 —
0xfe和0xff是同一字节字段中的不同哨兵:0xfe表示“尚未写入,保持不变”(幂等填充),0xff表示“在此交付”。使用单个带外值,或把0xff当作另一个链路索引的重实现,会误路由未设置行或目的行。
6. 函数映射
| 函数 | 地址 | 角色 |
|---|---|---|
ParallelRoutingTableGenerator::CreateUnicastRoutingTables | 0x1fbd5340 | fiber 化二维遍历驱动 |
RoutingTableGenerator::CreateUnicastRoutingTables | 0x1fbd94a0 | 串行遍历孪生实现 |
$_0 outer fiber lambda | 0x1fbd70c0 | 每个源一个 Bundle 任务 |
| inner dst lambda | 0x1fbd7240 | dst 循环 + SetChannelMerges + first-error |
ParallelRoutingTableGenerator::CreateSrcDestUnicastRoutingTable | 0x1fbd5640 | 逐 (src,dst) 条目构建 |
RoutingTableGenerator::WalkCreateSrcDestUnicastRoutingTable | 0x1fbd9580 | 串行逐 (src,dst) 孪生实现(仅实时路径) |
RoutingTableGenerator::PopulateRoutingTable | 0x1fbdb5c0 | 逐跳表选择 + 写入 |
RoutingTableGenerator::GetNextHopAction | 0x1fbda6a0 | hop → {next_chip, output_link, vc} |
GetTableIndex | 0x1fbdd000 | chip_id → dense row(crc32 swiss) |
GetEgressTable | 0x1fbdc040 | gen+0xa8[row],stride 0x48 |
GetNextHopTable | 0x1fbdbb00 | egress-next / link-next 选择 |
GetLinkHopTable | 0x1fbdbbe0 | 每 (chip, in-link) 表 |
RouteTargetCache::GetPath | 0x1fbd42c0 | [src_row][dst] 路径索引 |
SetChannelMerges | 0x1fbda1e0 | 每源 VC/channel 合并 |
GetVcBalanceUsage | 0x1fbdb4c0 | VC 均衡计数器(规则未完全化简) |
RoutingTableGeneratorFactory::CreateGenerator | 0x1fbd3dc0 | strategy → generator class |
7. 诊断源位置
全部通过 AddSourceLocationImpl / CreateStatusAndConditionallyLog 针对 parallel_routing_table_generator.cc 发出:
| 行 | 位置 |
|---|---|
| 371 / 373 | GetCoordinate(src) / GetCoordinate(dst) 错误 |
| 375 / 377 | GetEgressTable / GetNextHopTable 错误 |
| 384 | GetStaticPath 错误(实时路径) |
| 388 | 路径 num_hops 测试 |
| 391 / 394 / 396 | terminal 分支:egress / nexthop SetUnicastTerminal、SetUnicastVcControl |
| 401 / 403 / 405 | 多跳分支:HopDirection(0)、LinkMap::GetLink、SetUnicastTarget |
| 410 | PopulateRoutingTable 错误 |
| 417 | use_cache_ 一致性断言(LogMessageFatal) |
| 428 / 431 / 434 / 443 | cached 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 会穿过这些表。