chip_parts.binarypb 解码
本页中的所有地址、偏移和常量值,均适用于
libtpu-0.0.40-cp314wheel 中的libtpu.so(BuildID md589edbbe81c5b328a958fe628a9f2207d,未 strip,ELF x86-64)。其他版本会有所不同。
摘要
TPU 编译器需要的每个按代号区分的硬件常量——HBM/VMEM/SMEM/SFLAG 容量、MXU lane/sublane 几何、TensorCore 和 HBM 时钟、寄存器文件宽度、DMA 粒度——并不以 C++ 字面量承载,而是位于一个序列化 protobuf blob <codename>_chip_parts.binarypb 中,并直接嵌入 libtpu.so 的 .rodata 段。启动时,runtime 为当前活跃的 TpuVersion 解析出该 blob,将其解析为 TpuChipPartsProto,并把解码后的字段复制到 xla::jellyfish::Target 对象中,供 cost model、ISA emitter 和 topology 层读取。这是一个数据驱动的 HAL:同一个 C++ Target 类服务每一代硬件,只由加载进来的字节进行专门化。
这个机制类似 LLVM 的 TargetMachine 从 .td 生成的 SubtargetInfo 表初始化,只是这里的表是运行时加载的 proto,而不是 TableGen 烘焙好的 struct。proto schema 位于二进制自身的 protodesc_cold descriptor pool 中,因此可以精确恢复;这些 blob 每个为 232–624 字节,并且可以逐字节解码,无需推断。解析函数 TpuChipParts::DefaultsForVersion 根据 version 字符串构造 embed:// 资源路径,并通过 tsl::ReadBinaryProto 读取它。
对重新实现而言,约定是:
- 资源模型:九个
embed://tpu_chip_parts/<name>_chip_parts.binarypbblob 通过一个 40 字节FileWrapperdescriptor 数组注册,每个都带有磁盘上的 size、md5 fingerprint 和经重定位的数据指针。 - 解析路径:
version -> TpuVersionToString -> AsciiStrToLower -> StrCat("embed://...") -> ReadBinaryProto -> FromProto。 - proto schema:
TpuChipPartsProto及其配套的TpuCorePartsProto、TpuSequencerPartsProto、TpuMemoryPartsProto、TpuSharedMemoryPartsProto,以及tpu_chip_enums.proto枚举,作为按 field number 编排的表。
| Resolver | tpu::TpuChipParts::DefaultsForVersion @ 0x20b1b040 |
| 源文件(resolver) | learning/45eac/tpu/runtime/topology/tpu_chip_parts.cc:341 |
| Parser | tpu::TpuChipParts::FromProto @ 0x20b1b400; tpu::TpuMemoryParts::FromProto @ 0x20b333a0 |
| Descriptor 数组 | .data.rel.ro VA 0x22010ED0(file off 0x21E10ED0),9 entries × 0x28 字节 |
| Blob 区域 | .rodata VA 0x0BDF29A0..0x0BDF3AB8(VA == file offset) |
| Proto schema | protodesc_cold: tpu_chip_parts.proto @ 0xC18FD80 及配套文件 |
资源模型
目的
chip_parts 是某一代 TPU 硬件能力几何的唯一事实来源。它与 chip_config(见 TpuChipConfig)不是同一回事:chip_parts 描述硅片是什么(core 数量、内存大小、MXU 维度、时钟),而 chip_config 描述运行时模式(bounce buffer、同步标志资源、设备端传输窗口)。两者都是 embed:// proto blob,但通过不同函数解析,并解析到不同对象中。
.rodata 中的 Blob 布局
九个 blob 连续放在 .rodata 中,该 section 的 VA 等于 file offset,因此可以直接切出字节。每个 blob 都由 .data.rel.ro VA 0x22010ED0 处一个 9 项数组中的 40 字节 FileWrapper descriptor 注册。descriptor 布局是:
struct FileWrapper { // 0x28 bytes
const char* name; // +0x00 R_X86_64_RELATIVE reloc (0 on disk, filled at load)
const void* data; // +0x08 R_X86_64_RELATIVE reloc -> blob VA in .rodata
int64_t size; // +0x10 ON DISK (the serialized byte length)
uint8_t fp[16]; // +0x18 ON DISK (md5 fingerprint of the blob)
};
```text
> **GOTCHA —** `name` 和 `data` 指针在**磁盘镜像中读作零**:它们是由动态加载器在加载时填充的 `R_X86_64_RELATIVE` 重定位。只有 `size` 和 16 字节 md5 `fp` 是磁盘上的字面值。信任磁盘上指针字段的读取器会断定数组为空,并认为只有恰好被直接检查数据的两个 blob 存在。正确读取方式是解析 `data` 重定位的 *addend*(它指向真实 blob VA),并用磁盘上的 md5 校验它。九个 `fp` 字段中的每一个都匹配其重定位 addend 指向的 blob 的 md5。
>
> **NOTE —** 九个 blob 全部都已嵌入(上面的重定位陷阱让数组看起来很短):每个都已按其 descriptor 的 `fp` 字段完成 md5 验证并逐字节解码,因此每个按代号页面都携带确认值。
九个 descriptor 按数组顺序列出,并给出重定位解析后的 data VA:
下面的全部九个 `fp` 字段、`data` 重定位 addend、size 和 `version` 字节,均已独立重新推导:每个 blob 都在其重定位解析出的 `blob VA` 处切出、计算 md5,并与磁盘上的 `fp` 匹配——每行都匹配,因此每行都是 CONFIRMED。
| # | Resource key | desc VA | blob VA | size (B) | md5 (== `fp`) | first bytes |
|---|---|---|---|---:|---|---|
| 0 | `6acc60406_tensornode_chip_parts.binarypb` | `0x22010ED0` | `0x0BDF29A0` | 504 | `d1e4bea3…dec694a5` | `08 06` |
| 1 | `6acc60406_chip_parts.binarypb` | `0x22010EF8` | `0x0BDF2BA0` | 546 | `f5c490e6…02fd8029` | `08 06` |
| 2 | `ghostlite_chip_parts.binarypb` | `0x22010F20` | `0x0BDF2DD0` | 564 | `010c6352…13f5807f` | `08 05` |
| 3 | `viperfish_chip_parts.binarypb` | `0x22010F48` | `0x0BDF3010` | 601 | `fccc06a7…e84c9dcf` | `08 04` |
| 4 | `viperfish_lite_chip_parts.binarypb` | `0x22010F70` | `0x0BDF3270` | 232 | `a8e02254…064cb465` | `08 04` |
| 5 | `pufferfish_lite_chip_parts.binarypb` | `0x22010F98` | `0x0BDF3360` | 277 | `fb066c9a…d1ff501c` | `08 03` |
| 6 | `pufferfish_chip_parts.binarypb` | `0x22010FC0` | `0x0BDF3480` | 624 | `acdf3a9e…49af3fb2` | `08 03` |
| 7 | `jellyfish_chip_parts.binarypb` | `0x22010FE8` | `0x0BDF3700` | 435 | `f86192ba…c4adecda` | `08 01` |
| 8 | `dragonfish_chip_parts.binarypb` | `0x22011010` | `0x0BDF38C0` | 504 | `d3d51f67…80f61047` | `08 02` |
`first bytes` 列是 `tag=0x08`(field 1,varint),后跟 `TpuVersionProto` 值:jellyfish=1、dragonfish=2、pufferfish(+lite)=3、viperfish(+lite)=4、ghostlite=5、6acc60406=6。两个 `6acc60406` blob 只在 package multiplicity 上不同——`tensornode` 是一个 die(1 个 TensorCore、2 个 SparseCore、1 个 HBM stack);裸 `6acc60406` blob 是完整的双 die megachip(数量翻倍)。完整解码见 [按代号常量表](per-codename-hw-constants.md)。
### 解析路径
`DefaultsForVersion` 在运行时构造资源名并读取它。重建出的逻辑:
```c
StatusOr<TpuChipParts> DefaultsForVersion(TpuVersion v, string_view variant): // sub_20b1b040
proto = TpuChipPartsProto()
name = AsciiStrToLower(TpuVersionToString(v)) // e.g. "jellyfish", "6acc60406"
if variant.non_empty(): // tensornode / lite selector
StrAppend(&name, "_", variant)
filename = StrCat("embed://tpu_chip_parts/", name, "_chip_parts.binarypb")
status = tsl::ReadBinaryProto(Env::Default(), filename, &proto) // tpu_chip_parts.cc:343
CHECK(status == Ok) << "Failed to parse TpuChipPartsProto."
return TpuChipParts::FromProto(proto) // sub_20b1b400因此选择 v=0(jellyfish)会得到 jellyfish_chip_parts.binarypb 资源,v=4(ghostlite)会得到 ghostlite_… blob,依此类推。embed:// VFS scheme 将资源名映射回 name 字符串匹配的 FileWrapper;随后 ReadBinaryProto 解析该 blob 的字节。由于名称是从 version 计算出来的,每一代的 blob 都是活的,不是死数据——resolver 会为每一代构造有效 key。
NOTE —
DefaultsForVersion在 parse error 时会CHECK失败(fatal),而不是返回降级默认值。C++ 中没有烘焙的 fallback geometry;如果 blob 缺失或格式错误,runtime 会 abort。这就是数据驱动 HAL 背后的架构承诺:proto 就是硬件描述。
恢复出的 Proto Schema
完整 schema 从二进制的 protodesc_cold pool 中的 FileDescriptorProto 恢复(tpu_chip_parts.proto @ 0xC18FD80、tpu_core_parts.proto @ 0xC190810、tpu_sequencer_parts.proto @ 0xC191340、tpu_memory_parts.proto @ 0xC191750、tpu_shared_memory_parts.proto @ 0xC1919B0、tpu_chip_enums.proto @ 0xC191B90)。下面的每个 field number 都已通过按该 schema 解码嵌入 blob 确认;解码逐字节精确,并在全部九个 blob 上自洽。
TpuChipPartsProto — 顶层消息
下面的 field name、number、type 和 label 直接从 0xC18FD80 处的 tpu_chip_parts.proto FileDescriptorProto 读取(每行的 type_name/label 都在 descriptor 中),并且每个编号字段都已从全部九个 blob 中独立重新解码——因此每行都是 CONFIRMED。
| Field | # | Type | 含义 |
|---|---|---|---|
version | 1 | TpuVersionProto | generation 标签(1..6) |
cores | 2 | repeated Core | 每个 core type 一个 entry(TensorCore + BarnaCore/SparseCore) |
shared_memories | 3 | repeated SharedMemory | HBM(总是存在)和 CMEM(仅 Pufferfish) |
uhi_sync_flag_memory_parts | 4 | TpuMemoryPartsProto | UHI 同步标志区域(存在时) |
local_shared_memory_mappings | 5 | repeated LocalSharedMemoryMapping | core -> HBM stack 拓扑 |
dma_requirements | 6 | DmaRequirementsProto | alignment / granule / max-single-DMA |
variant_name | 7 | string | lite blob 上为 "lite",其他为空 |
misc | 8 | MiscPropertiesProto | 同步标志功能 flag |
driver_abi_version | 9 | int64 | 每一代上都是 1 |
嵌套和配套消息
Core { TpuCoreTypeProto type=1; TpuCorePartsProto parts=2; int32 count=3; }
SharedMemory { TpuSharedMemoryTypeProto type=1; TpuSharedMemoryPartsProto parts=2; int32 count=3; }
DmaRequirementsProto { int64 host_alignment_bytes=1; int64 device_alignment_bytes=2;
int64 granule_bytes=3; int64 sync_flag_granule_bytes=4;
int64 max_single_host_dma_bytes=5; }
MiscPropertiesProto { int32 max_slice_size_for_all_to_all_routing=1;
bool has_extra_done_bit_in_sync_flags=2;
bool is_host_sync_flag_access_async=3;
bool supports_sync_flag_mode_count_dones=4; }
TpuCorePartsProto { TpuVersionProto version=1; TpuCoreTypeProto type=2;
repeated Sequencer sequencers=3; repeated Memory memories=4;
int32 frequency_mhz=5; int32 host_interrupt_count=6;
BarnaCore barna_core=7; SparseCore sparse_core=8; }
Sequencer { TpuSequencerTypeProto type=1; TpuSequencerPartsProto parts=2; int32 count=3; }
Memory { TpuMemoryTypeProto type=1; TpuMemoryPartsProto parts=2; int32 count=3; }
SparseCore { int32 dreg_word_count=1; int32 dreg_bytes_per_word=2;
int32 tile_hbm_bandwidth_bytes_per_cycle=3; int32 stream_granule_size=4; }
TpuSequencerPartsProto { TpuVersionProto version=1; TpuSequencerTypeProto type=2;
repeated Register registers=3; ScalarIsa scalar_isa=4;
VectorIsa vector_isa=5; BarnaCoreFsm barna_core_fsm=6; }
Register { TpuRegisterTypeProto type=1; int32 count=2; }
VectorIsa { int32 lane_count=2; int32 sublane_count=3; int32 issue_latency_cycle_count=4;
int32 mxu_count=5; int32 xlu_count=6; int32 iar_count=7; }
TpuMemoryPartsProto { TpuVersionProto version=1; TpuMemoryTypeProto type=2;
bool holds_instructions=3; bool supports_dma=4;
int32 bytes_per_word=5; int64 word_base=6; int64 word_count=7;
int64 bundle_count=8; int64 bytes_per_instruction_dma_chunk=9;
int64 bundles_per_instruction_dma_chunk=10; }
TpuSharedMemoryPartsProto { TpuVersionProto version=1; TpuSharedMemoryTypeProto type=2;
int32 bytes_per_word=3; int64 word_count=4; int32 frequency_mhz=5;
int32 channel_count=6; int32 ports_per_channel=7;
int32 bytes_per_port=8; int64 bytes_per_second=9; }
```text
> **QUIRK —** memory 和 shared-memory 的大小存为 `bytes_per_word × word_count`,从不存为单个 byte count。`6acc60406` 上的 HBM 是 `32 × 3,187,671,040 = 102,005,473,280 B`(95 GiB);只读取 `word_count` 会因为 word 因子而低估。“word” 是该内存的原生访问粒度:v5p/v6e/v7 HBM 为 32 B,v2/v3 HBM 为 1024 B,所有 VMEM 为 512 B,SMEM/SFLAG 为 4 B。`TpuMemoryParts::FromProto` 会先验证每个字段(`bytes_per_word > 0`、`word_count > 0`、指令内存不得设置 `word_base`/`word_count` 等),再将 region 打包到 64 字节堆记录(`operator new(0x40)`)中。
>
> **GOTCHA(HBM/CMEM packing)—** HBM 和 CMEM 层级(`shared_memories` field 3)通过 `tpu::TpuSharedMemoryParts::FromProto` @ `0x20b34aa0` 解析,这是一条与 `TpuMemoryParts::FromProto` *分离*的路径;内存中的 record 与 proto wire form 有两点不同,重新实现者必须精确复现。**(1)** `bytes_per_word` 字段(`TpuSharedMemoryPartsProto` field 3)**不按字节存储**——`FromProto` 运行 `_BitScanReverse(bytes_per_word)`,并在 record offset `+0x20` 存储 **log2**(例如 `32 B → 5`、`512 B → 9`);byte count 必须重新计算为 `1 << log2`。**(2)** 解码后的 record 是一个 **48 字节**堆对象(`operator new(0x30)`),不同于 64 字节的 `TpuMemoryParts` record,其字段顺序为 `{ version:+0x00, type:+0x04, word_count:+0x08 (int64), frequency_mhz:+0x10, channel_count:+0x14, ports_per_channel:+0x18, bytes_per_port:+0x1c, bytes_per_word_log2:+0x20, bytes_per_second:+0x28 (int64) }`。validator(源文件 `tpu_shared_memory_parts.cc`)强制 **power-of-two `bytes_per_word` bounded to `[8, 32768]`**("Shared memories must have words between 8 and 32768 bytes")、`word_count > 0`、`frequency_mhz ≥ 0`、`channel_count ≥ 0`,以及 `ports_per_channel`/`bytes_per_port` 一致性规则(两者同时为零,或两者同时为正),然后再打包——因此,带 4 B word 的 HBM/CMEM blob(对经由 `TpuMemoryParts` 的 SMEM/SFLAG 合法)会在 shared-memory 路径中被拒绝。
### 相关枚举(`tpu_chip_enums.proto`)
```text
TpuCoreTypeProto : 1 TENSOR_CORE, 2 BARNA_CORE, 3 SPARSE_CORE
TpuSequencerTypeProto : 1 TC_SEQ, 2 BC_SEQ, 3 BC_ADDR, 4 SC_SEQ, 5 SC_TAC, 6 SC_TEC
TpuRegisterTypeProto : 1 SREG, 2 VREG, 3 PREG, 4 VMREG
TpuMemoryTypeProto : 1 IMEM, 2 VIMEM, 3 TILEIMEM, 4 SMEM, 5 SFLAG, 6 TACSFLAG,
7 TECSFLAG, 8 VMEM, 9 TILESPMEM, 10 SPMEM, 11 TACSMEM, 12 TECSMEM
TpuSharedMemoryTypeProto : 1 HBM, 2 CMEM
TpuVersionProto : 1=jellyfish 2=dragonfish 3=pufferfish 4=viperfish 5=ghostlite 6=6acc60406chip_parts 承载什么(以及不承载什么)
chip_parts 承载 Target 能力表面所需的一切:每个 memory size、每个 core/sequencer/register count、MXU lane_count/sublane_count/mxu_count/xlu_count/iar_count、TensorCore 和 HBM frequency_mhz、HBM bytes_per_second,以及 DMA granule/alignment block。有两类常量不在 proto 中:
- VMEM/SMEM/CMEM bank count。 这些是按代号
*Target::MemBanksoverride 中的 C++ 字面量,不是 proto 字段。见 按代号常量表。 issue_latency_cycle_count(VectorIsa field 4)在每个 blob 中都缺失(默认 0),包括全部六个较老 generation——真实的 MXU/VPU issue latency 位于按代号 cost model 中,而不是 chip_parts 中。MXU systolic depth 同样不是显式 proto 字段:proto 给出lane_count=128和mxu_count,但 v6e/v7 的 256×256 systolic 维度是单独的GhostliteTargetC++ override。
NOTE — 该 proto 中没有 dtype/sparsity 能力字段。每种格式的 peak FLOPS 和支持精度编码在按代号 ISA 与 accuracy 表中,而不在
chip_parts中。
相关组件
| 名称 | 关系 |
|---|---|
TpuChipConfig::Create | chip_config(mode/resource)blob 的并行 embed:// resolver,不是 capability geometry |
TpuChipParts::FromProto | 将 blob 解析为 TpuChipParts;填充 Target field block +0x438..+0x510 |
xla::jellyfish::Target | 解码后常量填充的 runtime 对象;由 cost model、ISA emitter、topology 读取 |
交叉引用
- 按代号常量表 — 全部九个 blob 作为按 generation 表的逐字节精确解码
- TpuChipConfig — 并行的
chip_configresolver,以及解码后常量填充的Targetfield block - 代号矩阵 — TpuVersion ↔ codename ↔ marketing-name 映射
- 按代比较矩阵 — 跨页面汇总的按 generation 比较
- FileWrapper TOC 目录 — 该 descriptor 数组所属的完整嵌入资源目录
- Cost Model 概览 — 解码出的 frequency 和 bandwidth 常量的消费者
- ISA 概览 — 解码出的 MXU lane/sublane 几何的消费者