拓扑发现
地址适用于
libtpu-0.0.40-cp314wheel 中的libtpu.so。其他版本会有所不同。二进制文件:extracted/libtpu-0.0.40-cp314-cp314-manylinux_2_31_x86_64/libtpu/libtpu.so(build-id89edbbe81c5b328a958fe628a9f2207d;.textVMA == 文件偏移)。完整符号二进制中存在符号;反修饰名称和地址已与libtpu.sov0.0.40 的 IDA 反编译结果交叉核对。
摘要
本文档说明 slice 控制器如何获知 TPU pod-slice 的物理环面形状:每个 worker 在阶段 1 上报的芯片本地连接关系如何折叠成一个全局拓扑,如何为每个芯片分配笛卡尔 (X,Y,Z) 坐标,以及如何把该坐标映射转换成 slice 范围内的整数 chip-id 并推回每个芯片。入口点是 accel_ssw::deepsea::slice_builder::Master::DiscoverTopology @0x1fbbe4e0,它驱动组合式 TopologyDiscoverer::Discover @0x1fbff7e0,随后在 Master 上安装一个 ResilientToroidalTopology。坐标和逐方向链路映射会成为 chip-id 分配(Master::SetGlobalChipId @0x1fbbe7e0)以及路由表生成的输入。
重新实现者必须内化的最重要事实是:发现过程不会发送主动探测。 当 DiscoverTopology 运行时,每个芯片的固件已经在 PHY/数据链路拉起时协商好了每个 SerDes 端口另一端是谁(远端 ChipLocation、远端端口名、轴向)。这个逐端口的“那里是谁”字段会在阶段 1 期间通过 LocalTopology proto 发送给 Master,它就是探测响应。因此,发现只是对已收集数据进行纯粹的图构建和校验,而不是一次网络交换。"ICI Probe failed..." 这个 .rodata 字符串属于拉起之后的 LinkChecker 健康探测,不属于这条路径。
本文负责三件事:(1) 发现探测,即 LocalTopology 图包含什么以及七步推断流程(TopologyDiscoverer::Discover);(2) 坐标↔芯片映射,即从原点芯片传播 (X,Y,Z) 的 BFS 以及验证该映射的冲突检查;(3) 全局 chip-id 分配,即如何把已验证的坐标映射转换为密集 0..N-1 id 并推送到每个芯片。链路拉起(固件 PHY / 主机 DL 分工和 16 步 Master::InitSlice)见 链路拉起;消费该输出的路由表生成见 路由表生成;章节地图见 ICI 概览。
对重新实现而言,契约是:
- 输入。 一个
vector<LocalTopology>(每个芯片一项,由阶段 1 的GetLocalTopology扇出收集),外加描述预期 slice 形状的目标ToroidalTopologyInterface。locals span 以Master+776/Master+784(begin/end)传入。 - 探测模型。 没有主动发现探测。每个
LocalTopology为每个端口携带固件解析出的remote_chip_location、remote_port、orientation(轴)、is_data_layer_connected,以及(在 3-D 部件上)polarity。发现会推断 2-D 部件上缺失的符号,构建全局链路映射,并进行校验。 - 输出。 安装在
Master+152的ResilientToroidalTopology,背后由一个flat_hash_map<ChipLocation, Coordinates>(坐标↔芯片映射,位于Master+832)和一个逐芯片map<Direction, PhysicalIciLink>(为路由播种的方向↔端口映射)支撑。 - Id 分配。
Master::SetGlobalChipId读取每个归属芯片的坐标,并向拓扑对象请求该芯片的密集整数 id(笛卡尔顺序,X 最快变化),然后把repeated ChipLocationToId列表发送给 worker。 - 幂等性。 重新发现会被拒绝(
already_discovered_guard);SetGlobalChipId要求芯片映射先已填充。
| 发现入口 | Master::DiscoverTopology @0x1fbbe4e0 → 内部 lambda $_0::operator() @0x1fbc1ae0 |
| 组合发现器 | TopologyDiscoverer::Discover @0x1fbff7e0(ctor @0x1fbff680);旧版回退 LegacyTopologyDiscoverer::Discover @0x213dcfe0 |
| 链路发现 | IciDiscoverer::Init @0x1fc09d40, IciDiscoverer::Discover @0x1fc0b720 |
| 坐标分配 | ChipCoordinatesAssigner::Assign @0x1fc00420, ::BreadthFirstWalk @0x1fc02040 |
| 极性分配 | superpod::routing::IciLinkPolarityAssigner::Assign @0x1fc0d880, ::ChooseSeed @0x1fc10cc0 |
| 坐标↔芯片映射 | flat_hash_map<ChipLocation, Coordinates> @Master+832;拓扑安装位置 @Master+152 |
| 全局 chip-id 推送 | Master::SetGlobalChipId(string_view, Stub*) @0x1fbbe7e0 |
| 坐标推送 | Master::SetChipCoordinates @0x1fbc4640 → WorkerService::SetChipCoordinates @0x1fc3db80 |
| 新模块开关 | flag tpu_slice_builder_topology_discovery_new_module(否则走旧路径) |
1. “探测”就是阶段 1,即 LocalTopology 图
DiscoverTopology 在 Master 已经对 slice 中每个 worker 调用过 GetLocalTopology 之后运行(Master::InitSlice 的第 1 步;完整阶段表见 链路拉起)。每个 worker 的 WorkerService::GetLocalTopology @0x1fc3c740 返回一个 LocalTopology proto 列表,该列表由芯片本地驱动从固件可读的 ChipConnectorInfo 寄存器集组装而成。Master 将它们折叠进自己在 Master+776/+784/+792 处持有的 vector<LocalTopology>,并把逐 worker 的芯片集合缓存在 Master+800 处的 flat_hash_map<string, flat_hash_set<ChipLocation>> 中。
这个 vector 就是发现的全部输入,因此内部 lambda 会把 locals vector 的 *(_QWORD*)(this+776)(begin)和 *(_QWORD*)(this+784)(end)直接作为 absl::Span<const LocalTopology> 传给 TopologyDiscoverer::Discover。
1.1 LocalTopology 线格式
accel_ssw::deepsea::proto::LocalTopology proto 携带单个芯片的连接快照。C++ 包装器(LocalTopology::LocalTopology(proto&) @0x1ffdb620)实体化一个 2560 字节记录(已确认:vector emplace 路径中有 operator new(2560 * count)):约 40 字节头部(ChipLocation + hostname),后接 12 个内联 208 字节 PortEntry 槽位(12 × 208 = 2496),num_ports 计数作为 int32 存在字节偏移 2552(*((int*)this + 638))处,即位于端口数组之后,而不是头部中。proto 中 kMaxPortsPerChip = 12,虽然当前 SerDes 硬件每芯片使用 4 个端口,见 ICI 概览 §1。
message LocalTopology {
optional asic_sw.proto.ChipLocation chip_location = 1; // tray/slot identity, e.g. "tray12-3"
optional string hostname = 2; // chip's host machine FQDN
optional int32 num_ports = 3; // 0..12 actual SerDes count (4 on JFC/DFC)
repeated PortEntry ports = 4; // num_ports entries
}
message PortEntry {
optional asic_sw.deepsea.PortName local_port = 1; // e.g. "ici_n0" — letter encodes lane + axis
optional asic_sw.proto.ChipLocation remote_chip_location = 2; // who is on the other end (set iff DL up)
optional asic_sw.deepsea.PortName remote_port = 3; // which remote port our cable plugs into
optional int32 port_index = 4; // hardware port index (0..3 on jfc)
optional bool is_data_layer_connected = 5; // DL came up; false = missing cable / PHY fail
optional accel_ssw.deepsea.proto.Orientation orientation = 6; // X / Y / Z axis (from SerDes connector ID)
optional accel_ssw.deepsea.proto.Polarity polarity = 7; // POSITIVE/NEGATIVE/UNKNOWN (UNKNOWN on 2-D)
optional bool is_high_latency = 8; // inter-tray cable; affects latency model
}
```text
> **注意 —** 字段*名称*是根据 C++ accessor 顺序和 `ByteSizeLong` 序列化顺序推断的;上面的数字 protobuf tag 是名义值(名称/顺序为高置信度,精确 tag 数字为低置信度,需要解码链接的 `FileDescriptorProto` blob 才能确认)。
从 `.rodata` 恢复出的枚举值映射如下:
| 枚举 | 值 |
|---|---|
| `Polarity` | `UNKNOWN_POLARITY=0`, `POSITIVE=1`, `NEGATIVE=2` |
| `Orientation` | `UNKNOWN_ORIENTATION=0`, `X=1`, `Y=2`, `Z=3` |
| `Direction`(轴 × 符号) | `kIciXPlus/XMinus/YPlus/YMinus/ZPlus/ZMinus`(存在字符串锚点;数字顺序低置信度) |
`orientation` 是**物理**属性,它内嵌在布线中,并由固件在链路两端以相同方式暴露。发现过程新增的是*符号*(polarity)、笛卡尔坐标、全局校验,以及路由消费的 direction→port 映射。`Direction::Opposite`(在 `IciDiscoverer::Init` 中内联引用)把每个 `Direction` 映射到符号翻转后的对应方向,这是每条双向链路都会被检查的 invariant(§2.3)。
---
## 2. `TopologyDiscoverer::Discover`:七步推断
`Master::DiscoverTopology` @`0x1fbbe4e0` 构造内部 lambda 捕获 `{this, &target_size, origin_coord}` 并调用 `$_0::operator()` @`0x1fbc1ae0`,后者驱动组合流程。组合式 `TopologyDiscoverer`(ctor @`0x1fbff680`)在固定指针偏移 +8..+40 处持有五个子对象:`IciLinkPolarityAssigner`、`ChipCoordinatesAssigner`、`IciDiscoverer`、`TopologyFaultVerifier`、`TrayShapeChecker`,并受 `tpu_slice_builder_topology_discovery_new_module` 控制。`LegacyTopologyDiscoverer` @`0x213dcfe0` 是回退路径(它额外加入一个 `SliceReshaper` 步骤和一次整体 walk,但产出相同的 `ResilientToroidalTopology`)。
> `Master::DiscoverTopology` @`0x1fbbe4e0`:从 `.rodata` 常量数组(`{4,4,4}`,不是 `Master` 字段)构造 3 维 `Coordinates`,捕获 `{this, &(*(int*)(this+304)), coord}`,其中 `this+304`(`*((int*)this + 76)`)是 target-size int,并调用 `$_0::operator()` 两次(一次正常调用,一次在可选故障注入后调用)。`$_0 != 1`(非 OK)路径返回 `CreateStatusAndConditionallyLog(668, "…/master.cc", …)`,也就是说包装错误 `"Failed to discover ICI network topology"` 在 **`master.cc:668`** 发出(原始锚点估计的 `:655` 已被反编译结果取代)。
```c
// TopologyDiscoverer::Discover(target_topology, locals_span, option) // 0x1fbff7e0
if (already_discovered_) // +48 guard
return FAILED_PRECONDITION // "Topology graph has already been discovered ..."
clone(locals_span) -> owned vector<LocalTopology> // stages mutate the per-port Direction in place
// STEP 2-4: polarity (only on 2-D parts)
if (IciLinkPolarityAssigner::IsPolarizationNeeded(tpu_type)): // 0x1fc0d7a0 — binary search kTpusWith2dSlices
IciLinkPolarityAssigner::Assign(target, locals): // 0x1fc0d880
Init() // bucket links by (Chip, Orientation)
seed = ChooseSeed() // first chip forming a 2x2 square
BreadthFirstWalk(seed): // propagate +/- across bidirectional pairs
AssignOrVerifyPolarity(chip) // opposite signs on the two endpoints of a link
UpdatePolarizedLocalConnectivity(chip) // rewrite Orientation -> signed Direction
locals = GetUpdatedLocals() // fresh vector, every port now signed
// STEP 5: link discovery + validation
IciDiscoverer::Init(target, locals); IciDiscoverer::Discover(target, &chip_to_coord, locals) // 0x1fc09d40 / 0x1fc0b720
// STEP 6: coordinate BFS
ChipCoordinatesAssigner::Init(target, locals); ::Assign(); ::BreadthFirstWalk() // 0x1fc00580 / 0x1fc02040
// STEP 7: validation
TopologyFaultVerifier::Verify(); TrayShapeChecker::Check()
install ResilientToroidalTopology on Master+1522.1 极性开关:IsPolarizationNeeded
IciLinkPolarityAssigner::IsPolarizationNeeded(TpuType) @0x1fc0d7a0 对一个有序 .rodata 数组 kTpusWith2dSlices 做二分搜索。只有在 slice 是 2-D torus 的 TPU 代际上它才返回 true,因为固件在这些代际中报告每个端口的轴但不报告符号。在 3-D 部件上,线缆 ID 会显式携带符号,整个极性 pass 会被跳过。
原因 — orientation 是对称的:固件知道某个端口在 X 轴上,但无法说哪一端是
X+。唯一能唯一钉住符号的结构是闭环。orientation 图中的 2×2 方块恰好有一种一致的符号分配(某条边为 X+,相对边为 X−;Y 同理),因此 assigner 会寻找一个方块 seed 并向外传播。
2.2 方块 seed + BFS 极性(ChooseSeed / BreadthFirstWalk)
ChooseSeed @0x1fc10cc0 扫描 slice 的芯片;对每个芯片,它调用 FindCorners(loc) @0x1fc11fa0,后者使用 FindLinksByOrientation @0x1fc15140 拉取该芯片的 X 轴和 Y 轴链路,并寻找一对 (x_link, y_link),其两个远端会收敛到同一个第四芯片,也就是一个 2×2 方块。第一个形成方块的芯片就是 seed;如果不存在:
"2D slice's ICI link polarity assignment fails because no seed chip is found that forms a square"
-> FAILED_PRECONDITION (ici_link_polarity_assigner.cc:258)
```text
`BreadthFirstWalk(seed)` @`0x1fc11040` 将 seed 入队,调用 `AssignOrVerifyPolarity(seed, is_seed=true)` @`0x1fc129a0`(按约定固定四条 seed 极性),随后对每个后续芯片调用 `FindMinimalSetOfLinksToPolarize` @`0x1fc15620`(从已极化邻居推断该芯片的符号,即每条链路两端符号相反)、`AssignOrVerifyPolarity(chip, false)`,以及 `UpdatePolarizedLocalConnectivity(chip)` @`0x1fc13be0`(把每个端口记录从原始 `Orientation` 重写为带符号的 `Direction`)。如果从第二条路径重新访问时出现不一致,会发出:
```text
"ICI link <%s, %s> has assigned polarity which is invalid pre-condition for assigning polarities"
-> INTERNALwalk 结束后,每个端口都有完全解析的 Direction(轴 + 符号),每个双向 pair 在两端符号相反,且没有端口仍为 UNKNOWN polarity。
2.3 链路发现 + 反向对应检查(IciDiscoverer)
IciDiscoverer::Discover@0x1fc0b720的签名接受(ToroidalTopologyInterface&, flat_hash_map<asic_sw::ChipLocation, superpod::routing::Coordinates>*, Span<LocalTopology>),也就是说它的第二个参数是坐标↔芯片映射,确认 discoverer 会直接写入之后被 id 分配读取的映射。
IciDiscoverer::Init @0x1fc09d40 遍历每个芯片 × 端口。对每个端口,它解析远端名称(LocalTopology::GetRemoteName @0x1ffdd1a0)并执行:
- Loopback / 未连接:如果没有远端:当
is_data_layer_connected == 0时记录VLog(1) "ICI port %s does not have a remote port connected. Ignoring this link for coordinate assignment."(静默丢弃);否则记录"ICI port %s is incorrectly left in loopback mode. Ignoring this link for ICI links discovery."(也丢弃)。 - 未知轴/符号:
Orientation == UNKNOWN→"ICI link <%s, %s> has unknown orientation which is invalid"(ici_discoverer.cc:76);Polarity == UNKNOWN→"...has unknown polarity which is invalid"(ici_discoverer.cc:82)。两者都是 INVALID_ARGUMENT。 - 有效链路:构造
PhysicalIciLink{local_chip, local_port, remote_chip, remote_port, polarity, orientation, is_high_latency},并插入一个按本地芯片、再按带符号Direction索引的map<ChipLocation, map<Direction, PhysicalIciLink>>。重复的ChipLocation会被拒绝:"Chip %s is not unique during ICI links discovery for topology %s."(ici_discoverer.cc:39,INVALID_ARGUMENT)。
第二个 pass 会针对其反向检查每个 (chip, direction):查找远端芯片,计算 Direction::Opposite(direction),并确认远端带有一条链路指回我们,且 remote_chip == us、remote_port == our local_port。不匹配时:
"Bidirectional ICI link <%s, %s> with direction %c does not have a reverse counterpart during discovery."
-> INTERNAL (ici_discoverer.cc:121)
```text
最后,**节点数量**必须匹配意图:`target.GetTopologySize() == locals.size()`,否则报 `"Topology intent %s with %d nodes does not match the slice's chip-local ICI connectivity input which has %d nodes"`(`ici_discoverer.cc:139`,FAILED_PRECONDITION)。
---
## 3. 坐标↔芯片映射:从原点开始 BFS
`ChipCoordinatesAssigner::Init` @`0x1fc00580` 会重新校验链路集,并额外跟踪一个 `flat_hash_set<string>` seen names,用于捕获与 `ChipLocation` 检查不同的 hostname 碰撞(`"Chip %s is not unique during node coordinate assignment for topology %s."`,`chip_coordinates_assigner.cc:91`)。核心是 `BreadthFirstWalk` @`0x1fc02040`。
> `ChipCoordinatesAssigner::BreadthFirstWalk` @`0x1fc02040`:使用位于 `this+80` 的 `std::deque<asic_sw::ChipLocation>`,对原点执行 `emplace_back`,对当前芯片执行 `pop_front`,随后对每个方向调用 `FindNeighbor` @`0x1fc03ba0` 和 `AssignOrVerifyCoordinates` @`0x1fc03da0`。deque 驱动的 BFS 与下面重构完全一致。
```c
// ChipCoordinatesAssigner::BreadthFirstWalk() // 0x1fc02040
dirs = target.GetAllDirections() // 4 entries on 2-D, 6 on 3-D
deque.emplace_back(origin) // origin seeded from assigner+56 (the first chip of the discovered link set)
chip_to_coord_[origin] = Coordinates(0,0,0) // chip_to_coord_ at assigner+24
while (!deque.empty()):
cur = deque.pop_front()
for d in dirs:
neighbor = FindNeighbor(cur, d) // 0x1fc03ba0 — no neighbor => NOT_FOUND (cc:293)
AssignOrVerifyCoordinates(cur, neighbor, d) // 0x1fc03da0
if neighbor in chip_to_coord_: VerifyCoordinateConsistency(...) // 0x1fc04ba0
else:
neighbor_coord = cur_coord + target.GetCoordinateOffset(d) // topology supplies the offset
chip_to_coord_[neighbor] = neighbor_coord // VLog(3) "Assigned %s with coordinate %s."
deque.emplace_back(neighbor)
NormalizeChipPositions() // 0x1fc02ca0 — shift origin to (0,0,0)
(optional) RotateToroidalTopology() // 0x1fc040a0 — axis_swap from TopologyDiscoveryOption坐标类型。 superpod::routing::Coordinates(ctor Coordinates(array<int,6>&, int dims) @0x20c0b3c0)是 struct{ int32 dimensions_; int32 values_[6]; },支持 1..6 维(kMaxDimensionSize = 6),对 kIciXMinus..kIciZPlus enum 默认使用 3 维。
逐方向偏移。 discoverer 从不硬编码 X+ = (+1,0,0);它会询问 ToroidalTopologyInterface::GetCoordinateOffset(d)。自然单位偏移是:
kIciXPlus -> (+1, 0, 0) kIciYPlus -> ( 0,+1, 0) kIciZPlus -> ( 0, 0,+1)
kIciXMinus -> (-1, 0, 0) kIciYMinus -> ( 0,-1, 0) kIciZMinus -> ( 0, 0,-1)
```text
……但对 twisted torus,拓扑对象可以在 wrap 边界注入跨轴 delta(例如 `twist=1` 时 `Z+` wrap 到 `(-1,0,+1)`)。发现通过委托给拓扑来保持 twist 无关,见 **[Twisted Torus](../twist/overview.md)**。
### 3.1 冲突检测:`VerifyCoordinateConsistency`
`VerifyCoordinateConsistency` @`0x1fc04ba0` 使映射保持良构。当 BFS 从新方向到达一个已分配坐标的芯片时,它计算 `(claimed_neighbor_coord − cur_coord)`,按拓扑的逐轴大小(`GetTopologySizeByDim(i)`)取模,并要求它等于方向 `d` 的单位偏移。不匹配时:
```text
"Discovered conflicting cartesian coordinates assignment viewing from different paths in the slice's
toroidal ICI network: chip %s (coordinate %s) tries to assign chip %s with coordinate %s along
direction %c, whereas it has existing assigned coordinate %s."
-> INVALID_ARGUMENT (chip_coordinates_assigner.cc:325)这是典型的发现时失败,即误接线 torus 中两条路径对某个芯片位置产生分歧。缺失的必需方向则会从 FindNeighbor 暴露出来:
"ICI link direction %c is not eligible for topology discovery from chip %s"
-> NOT_FOUND (chip_coordinates_assigner.cc:293)
```text
### 3.2 断连、归一化、旋转
BFS 结束后,如果 `|chip_to_coord_| < target node count`,未访问名称会被收集并报告:`"Coordinate assignment failed ... because there are chips disconnected from the rest of the slice: %s."`(`chip_coordinates_assigner.cc:262`,FAILED_PRECONDITION)。
> **易踩点 —** 原点**不一定**是角点。BFS seed 是发现链路集中的第一个芯片(从 `assigner+56` 读取,来自 `Init` 期间的 locals),它可能位于 slice 中间,因此坐标在 walk 过程中可能变成负数。`NormalizeChipPositions` @`0x1fc02ca0` 会计算整个映射的逐分量最小值,并平移所有坐标,使较低角变为 `(0,0,0)`。如果重新实现者假设原点就是较低角,会推导出错误的 chip id(§4),而 chip id 是按*归一化后*的坐标排序的。
`RotateToroidalTopology` @`0x1fc040a0` 可选地旋转整个坐标集(X→Y→Z),前提是通过 `TopologyDiscoveryOption` 传入了 `axis_swap`。
### 3.3 第 7 步:故障和 tray 校验
`TopologyFaultVerifier::Verify` @`0x1fc0c6c0` 会将 discoverer 标记为 `is_fault=true` 的链路(目标形状期望但观测为断开的端口)与 `TopologyDiscoveryOption` 中接受的故障模式比较。随后 `TrayShapeChecker::Check` @`0x1fc0d000` 通过 `CheckMultiTrayShape` @`0x1fc0d320`,根据主机预期的 tray 布局(例如每主机 2×2 / 4×4 mini-mesh)验证发现到的 `ChipLocation` 集合。两者均通过后,`ResilientToroidalTopology` 才会安装到 `Master+152`。
> **注意 — 故障注入。** `Master::DiscoverTopology` 读取字符串 flag `tpu_slice_builder_topology_discovery_fault_injection`,通过 `Orientation_descriptor` + `proto2::internal::ParseNamedEnum` 解析它,并在值有效时用拓扑、locals 槽位(`Master+776`)、坐标/id 槽位(`Master+832`/`+840`)、解析出的 orientation,以及一个哨兵 `Coordinates` 调用 `InjectIciResilientFaults(...)`,随后在注入后的拓扑上重新运行 discovery lambda。这是生产路径上唯一的故障旋钮;无效字符串会产生 `"Invalid input fault injection dimension %s"`(`master.cc:680`,INVALID_ARGUMENT)。发现中的其他部分都是确定性的。
---
## 4. 全局 chip-id 分配:坐标映射 → 密集 id → 芯片
坐标↔芯片映射安装后,slice 仍然需要为每个芯片分配一个密集整数 id `0..N-1`(collectives、routing 和 firmware 都按它索引)。`Master` 通过笛卡尔顺序从已验证坐标推导 id,**X 最快变化,然后 Y,然后 Z**,并通过 `Master::SetGlobalChipId(string_view name, Stub*)` @`0x1fbbe7e0` 按 worker 推送。
> `Master::SetGlobalChipId` @`0x1fbbe7e0` 反编译:`lock_shared(Master+760)` → 在 `Master+800` 处的 `flat_hash_map<string, flat_hash_set<ChipLocation>>` 中查找该 worker 的芯片集合 → 对每个归属芯片,在 **`Master+832`** 处的 `flat_hash_map<ChipLocation, Coordinates>`(坐标↔芯片映射)中执行 `find<ChipLocation>` → 调用 `Master+152` 处 `ResilientToroidalTopology` 的 **vtable slot +144** 获取该芯片的 id(作为 `StatusOr<int>` 返回)→ 发出 `SetGlobalChipIdRequest_ChipLocationToId{ChipLocation (via ToProto), int id}` → 通过 stub vtable 发起 gRPC 调用。已与二进制逐项确认。
```c
// Master::SetGlobalChipId(worker_name, stub) // 0x1fbbe7e0
lock_shared(Master+760)
chips = (*flat_hash_map<string, set<ChipLocation>>)(Master+800)[worker_name] // worker's owned chips
req = SetGlobalChipIdRequest{}
for chip in chips:
if !chip_to_coord_.contains(chip): continue // chip_to_coord_ at Master+832
id_or = topology_at(Master+152)->GetGlobalChipId(chip) // vtable slot +144 -> StatusOr<int>
if (!id_or.ok()):
return "Failed to get global ID for chip <name> at <coordinate>" // builds coordinate via ToString
entry = req.add_entries() // ChipLocationToId
entry.chip_location = chip.ToProto()
entry.chip_id = id_or.value()
if (no entry added):
return NOT_FOUND "Failed to find any global ID for chips on worker <name>" // master.cc:1065, MakeErrorImpl<5>
unlock_shared(Master+760)
stub->SetGlobalChipId(ctx, req, &reply) // master.cc:1077 wraps gRPC failureworker 侧处理器 Worker::SetGlobalChipId 最终驱动芯片本地驱动 SliceConfiguration::SetSliceChipId(int),后者把 id 写入芯片本地 firmware(驱动层还暴露 QueueBasedGlobalConfig::SetGlobalChipId(int) / pxc::GlobalConfig::SetGlobalChipId(int))。
特性 — id 不存储在单独映射中。 自然的设计可能会保留一个
flat_hash_map<ChipLocation, int> chip_id_map_。二进制显示并非如此:Master+832持有的是坐标映射(ChipLocation → Coordinates),整数 id 由拓扑对象的 vtable slot +144 按芯片坐标即时产生。Master上没有独立的 id 表;拓扑是坐标和 id 的唯一真源。
4.1 伴随步骤:SetChipCoordinates
另外,Master 会通过 Master::SetChipCoordinates @0x1fbc4640 → WorkerService::SetChipCoordinates @0x1fc3db80 把原始 (X,Y,Z) 坐标推送到每个芯片。它会先检查芯片映射已初始化:
"chip_map_ has not been initialized.Did DiscoverTopology execute correctly?" (master.cc:1367)
```text
这是显式的顺序契约:发现必须先填充坐标映射,然后才能发送 id 或坐标。
> **注意 — 顺序。** `DiscoverTopology`(坐标)→ `SetGlobalChipId`(密集 id)→ 路由表生成/`SetRoutingTable` → `SetChipCoordinates`。坐标供给 id;二者都供给路由。完整逐步 `Master::InitSlice` 序列见 **[链路拉起](link-bringup.md)**。
---
## 5. 失败目录
从 `.rodata` 收集到的所有发现时诊断、状态码和源码行。路由时和恢复语义由 **[路由表生成](../routing/route-table-generation.md)** 与 **[故障恢复](failure-recovery.md)** 负责。
| 阶段 | 消息(节略) | 状态 | 来源 |
|---|---|---|---|
| 发现包装器 | re-discover: `"Topology graph has already been discovered ..."` | FAILED_PRECONDITION | `topology_discoverer.cc:69` |
| 发现包装器 | `"Failed to discover ICI network topology"`(包装内部错误) | (传播) | `master.cc:668` |
| 故障注入 | `"Invalid input fault injection dimension %s"` | INVALID_ARGUMENT | `master.cc:680` |
| 极性 | `"2D slice's ICI link polarity assignment fails because no seed chip ... forms a square"` | FAILED_PRECONDITION | `ici_link_polarity_assigner.cc:258` |
| 极性 | `"ICI link <%s, %s> has assigned polarity which is invalid pre-condition ..."` | INTERNAL | `ici_link_polarity_assigner.cc` |
| 极性 | `"Expected topology size %d does not match observed number of local ICI connectivity metadata %d."` | FAILED_PRECONDITION | `ici_link_polarity_assigner.cc:64` |
| 链路发现 | `"Chip %s is not unique during ICI links discovery for topology %s."` | INVALID_ARGUMENT | `ici_discoverer.cc:39` |
| 链路发现 | `"ICI link <%s, %s> has unknown orientation which is invalid"` | INVALID_ARGUMENT | `ici_discoverer.cc:76` |
| 链路发现 | `"ICI link <%s, %s> has unknown polarity which is invalid"` | INVALID_ARGUMENT | `ici_discoverer.cc:82` |
| 链路发现 | `"Bidirectional ICI link ... does not have a reverse counterpart during discovery."` | INTERNAL | `ici_discoverer.cc:121` |
| 链路发现 | `"Topology intent %s with %d nodes does not match ... which has %d nodes"` | FAILED_PRECONDITION | `ici_discoverer.cc:139` |
| 坐标 | `"Chip %s is not unique during node coordinate assignment for topology %s."` | INVALID_ARGUMENT | `chip_coordinates_assigner.cc:91` |
| 坐标 | `"ICI link direction %c is not eligible for topology discovery from chip %s"` | NOT_FOUND | `chip_coordinates_assigner.cc:293` |
| 坐标 | `"Discovered conflicting cartesian coordinates assignment ..."` | INVALID_ARGUMENT | `chip_coordinates_assigner.cc:325` |
| 坐标 | `"Coordinate assignment failed ... chips disconnected from the rest of the slice: %s."` | FAILED_PRECONDITION | `chip_coordinates_assigner.cc:262` |
| Id 推送 | `"Failed to get global ID for chip <name> at <coord>"` | (传播) | `master.cc` |
| Id 推送 | `"Failed to find any global ID for chips on worker <name>"` | NOT_FOUND | `master.cc:1065` |
| 坐标推送 | `"chip_map_ has not been initialized.Did DiscoverTopology execute correctly?"` | FAILED_PRECONDITION | `master.cc:1367` |
仅 VLog(静默丢弃,不是错误):`"ICI port %s is incorrectly left in loopback mode. ..."`、`"ICI port %s does not have a remote port connected. ..."`、`"ICI link <%s ,%s> is ignored by discovery because it is not connected to another chip in this slice."`。
---
## 6. 验证说明
> 已与 `libtpu.so` v0.0.40 的 IDA 反编译结果交叉核对:
>
> - `Master::DiscoverTopology` @`0x1fbbe4e0`:内部 `$_0` lambda 调用(把 `Master+152` 拓扑以及 `Master+776`/`+784` locals begin/end 传入 `Master+224` 处的 discoverer)、从 `.rodata` 常量 `{4,4,4}` 构造的 3 维 `Coordinates`、`master.cc:668` 处的 `!= 1` 错误、`tpu_slice_builder_topology_discovery_fault_injection` flag 读取、`Orientation_descriptor` + `ParseNamedEnum`、`InjectIciResilientFaults(...)`,以及 `master.cc:680` invalid-dimension 错误,均存在且匹配。
> - `TopologyDiscoverer::Discover` @`0x1fbff7e0` 和 `LegacyTopologyDiscoverer::Discover` @`0x213dcfe0`:二者都存在,并带有精确的 `(ToroidalTopologyInterface&, Span<LocalTopology>, TopologyDiscoveryOption&)` 签名。
> - `IciDiscoverer::Discover` @`0x1fc0b720`:第二个参数已确认为 `flat_hash_map<asic_sw::ChipLocation, superpod::routing::Coordinates>*`(坐标↔芯片映射)。
> - `ChipCoordinatesAssigner::BreadthFirstWalk` @`0x1fc02040`:`this+80` 处的 `std::deque<ChipLocation>`、`emplace_back`/`pop_front`/`FindNeighbor`/`AssignOrVerifyCoordinates`,deque 驱动的 BFS 已确认。
> - `Master::SetGlobalChipId` @`0x1fbbe7e0`:`lock_shared(+760)`、`+800` 处的 worker-chip-set map、`+832` 处的坐标映射、用于 id 的拓扑 vtable slot +144、`ChipLocationToId` request 构建、`master.cc:1065` NOT_FOUND,以及 `master.cc:1077` 处的 gRPC dispatch,均已精确确认。
> - `superpod::routing::IciLinkPolarityAssigner` 已确认位于 `superpod::routing` namespace 下(`ChooseSeed` @`0x1fc10cc0`,`Init` @`0x1fc0db40`),与原始源码路径暗示一致。
>
> **[低置信度]** `LocalTopology`/`PortEntry` 的数字 protobuf 字段 tag(名称 + 序列化顺序为高置信度;tag 需要解码链接的 `FileDescriptorProto`)。`Direction` enum 的数字值分配(`kIciXPlus = ?`):字符串锚点确认 value→name 集合,但整数顺序由 `NameOfDenseEnum<&Direction_descriptor>` 在运行时生成,不是 `.rodata` 字面量。BFS 原点 `ChipLocation`(从 `assigner+56` 读取)来自发现链路集中的第一个芯片;该 assigner 的 `Init` 使用的精确选择规则在本轮未完全反编译。`FindMinimalSetOfLinksToPolarize` @`0x1fc15620` 和每种拓扑形状(`TwistedTorus` 等)的 `GetCoordinateOffset` 的精确函数体在本轮未逐一反编译。
---
## 交叉引用
### ICI 章节页面
- [ICI 概览](overview.md) — 章节地图;发现位于 16 步 `Master::InitSlice` 拉起流程中的位置
- [链路拉起](link-bringup.md) — 固件 PHY / 主机 DL 分工、逐阶段 `InitSlice` RPC 表、`GetLocalTopology` 收集(阶段 1,即“探测交换”)
- [DMA 描述符](dma-descriptor.md) — 已发现/已路由 torus 承载的远程写描述符
- [故障恢复](failure-recovery.md) — `SliceFailureType`、`FailDevice` 级联、`LinksDownReset`(发现错误落点)
### 相邻章节
- [路由表生成](../routing/route-table-generation.md) — 消费这里产出的坐标↔芯片映射 + 方向↔端口映射
- [路由概览](../routing/overview.md) — `(src,dst) → link path`,基于已发现 torus 的静态维度顺序路由
- [Twisted Torus](../twist/overview.md) — 提供坐标 BFS 查询的 `GetCoordinateOffset`(带 twist delta)
- [返回索引](../index.md)