nf_descriptor(27 字段 Node-Fabric DMA 描述符)
本页所有地址均适用于
libtpu-0.0.40-cp314wheel 中的libtpu.so(build-id89edbbe81c5b328a958fe628a9f2207d,构建libtpu_lts_20260413_b_RC00)。该二进制带有完整 C++ 符号;.textVMA == 文件偏移,基址0xe63c000。其他版本会有所不同。
摘要
nf_descriptor_trace_entry 是一个分阶段 Node-Fabric DMA 描述符的线上 trace:它是 deepsea fabric 为把一个 buffer 从某个 chip 上的源端点移动到另一个(或在 multicast 情况下多个)chip 上的目标端点而 staging 的记录。"Node-Fabric" (nf) 是位于路由调度之下的跨节点 DMA 层:route table 回答“对此目标 chip 使用哪个输出链路”,net_router schedule 回答“在 step k,对这个 src→dst 执行 DMA”,而 nf_descriptor 命名的是实际的 {src_offset, src_resource, src_node, src_chip} → {dst_offset, dst_resource, dst_node, dst_chip} 传输、它的长度、multicast/segmented 标志,以及它触发的三个独立 SyncFlag completion。该描述符是 deepsea OCI SHAPE-B DmaDescriptor 的 jxc 对应物,也是 jxc::DmaDescriptor V1 记录的线上形式。
该记录是一个 proto2 消息 asic_sw::driver::deepsea::jxc::nf_descriptor_trace_entry,即 PerformanceTraceEntry oneof 的 case 3。它有 27 个字段,除字段 1(id,一个 TracePoint enum)和字段 4(descriptor_source,一个 descriptor_source_value enum)外,全部为 uint32。C++ 内存布局不是 proto 字段顺序:字段 1–3 和 5–27 从偏移 0x18 到 0x7c 连续布局,但字段 4(descriptor_source)被放在线外偏移 0x80,并带有非零内存默认值 1(BARNA_CORE)。这两个事实在下文被固定了两次:一次来自序列化器的 offset/wire-tag 对,一次来自 ::Clear 的清零块。
本页逐字段记录 27 字段布局及权威 C++ 偏移;三个 SyncFlag-update 通道(Source = “buffer free”,Destination = “data arrived”,Ack = “completion”)及其共享的 23 位 SyncFlagTarget 打包;HIB host-DMA 通道(字段 26/27);GetDmaSize / GetDmaTransactionId 访问器;相关的 bcs_internal BarnaCore-sequencer band;以及 deepsea GetDmaId 位编码器,它证明跨代 DMA 配对键是相同的逻辑 (tag, resource/core, node, chip) 元组,只是在现代硅片上被扩宽。承载这些描述符的 ICR DMA band 见 ICR Node-Fabric DMA;解析多跳链路路径的路由表见 Route-Table Generation。
对于重新实现,契约如下:
- 27 字段记录:每个 proto 编号、名称、类型、wire tag 和 C++ 内存偏移,包括位于
0x80、默认值为BARNA_CORE的线外descriptor_source。 - 三个 SyncFlag 通道及其 23 位 target pack
{sync_flag[0:10] | resource[10] | node[11] | chip[12:23]},包括显式的 Destination pack 以及生成相同布局的Source/AckOCI fold。 - HIB 通道(字段 26/27),它门控描述符是否还会触发 host-interface flag/ack。
- deepsea
GetDmaId38 位复合键transaction_id[0:21] | core_id[21:24] | chip_id[24:38],它逐字段与 jxc 27 位键交叉对齐,证明跨代配对等价性以及 chip 字段从 11→14 的扩宽。
| 消息 | asic_sw::driver::deepsea::jxc::nf_descriptor_trace_entry — PerformanceTraceEntry oneof case 3 |
| 字段数 | 27(uint32 × 25,TracePoint enum × 1,descriptor_source_value enum × 1) |
| C++ body | 0x18–0x7c 连续;descriptor_source 位于线外 0x80(默认 1) |
| hasbits | 0x10(4 字节) |
| 序列化器 | _InternalSerialize @0x1cf0ed20;Clear @0x1cf0eca0;_table_ @0x21c55ba0 |
| SyncFlag 通道 | Source @0xf6982e0 · Destination @0xf698340 · Ack @0xf6983a0(+ HIB 字段 26/27) |
| SyncFlagTarget pack | 23 位:sync_flag[0:10] | resource[10] | node[11] | chip[12:23] |
| Size / id 访问器 | GetDmaSize @0xf6982a0(length≪10)· GetDmaTransactionId @0xf698260 |
| deepsea 配对键 | GetDmaId(int) @0xf699ca0 — 38 位 txid[0:21]|core[21:24]|chip[24:38](chip mask 0x3fff) |
1. 27 字段记录
目的
该描述符端到端描述一次跨节点 fabric 传输:两个端点(offset + resource + node + chip)、字节长度、两个布尔 shape 标志(multicast / segmented),以及三个独立的 SyncFlag-raise 通道。每个已 staging 的描述符会发出一个 trace event;描述符的逐引擎执行(command issue、data-end)由 ICR Node-Fabric DMA 上的 nf band(case 6)追踪。
布局
下面的 C++ 偏移由 _InternalSerialize @0x1cf0ed20 精确到字节:每个字段都表现为一次 *((_DWORD *)this + N) 读取,并与写入输出流的 proto wire tag 成对出现(单字节 tag 使用 *a2 = TAG,字段 ≥16 的双字节 tag 使用 *(_WORD *)a2 = TAG)。descriptor-pointer 索引 N 映射到字节偏移 4*N。
| proto# | Name | Type | C++ off | Wire tag |
|---|---|---|---|---|
| 1 | id (TracePoint) | enum | 0x18 | 0x08 |
| 2 | tensor_node | uint32 | 0x1c | 0x10 |
| 3 | trace_id | uint32 | 0x20 | 0x18 |
| 4 | descriptor_source | enum | 0x80 | 0x20 |
| 5 | node_id | uint32 | 0x24 | 0x28 |
| 6 | chip_id | uint32 | 0x28 | 0x30 |
| 7 | program_counter | uint32 | 0x2c | 0x38 |
| 8 | source_offset | uint32 | 0x30 | 0x40 |
| 9 | source_resource | uint32 | 0x34 | 0x48 |
| 10 | destination_offset | uint32 | 0x38 | 0x50 |
| 11 | destination_resource | uint32 | 0x3c | 0x58 |
| 12 | destination_node_id | uint32 | 0x40 | 0x60 |
| 13 | destination_chip_id | uint32 | 0x44 | 0x68 |
| 14 | length | uint32 | 0x48 | 0x70 |
| 15 | destination_is_multicast | uint32 | 0x4c | 0x78 |
| 16 | destination_is_segmented | uint32 | 0x50 | 0x80 01 |
| 17 | destination_update | uint32 | 0x54 | 0x88 01 |
| 18 | destination_update_sync_flag | uint32 | 0x58 | 0x90 01 |
| 19 | destination_update_resource | uint32 | 0x5c | 0x98 01 |
| 20 | source_update | uint32 | 0x60 | 0xa0 01 |
| 21 | source_update_sync_flag | uint32 | 0x64 | 0xa8 01 |
| 22 | source_update_resource | uint32 | 0x68 | 0xb0 01 |
| 23 | ack_update | uint32 | 0x6c | 0xb8 01 |
| 24 | ack_update_sync_flag | uint32 | 0x70 | 0xc0 01 |
| 25 | ack_update_resource | uint32 | 0x74 | 0xc8 01 |
| 26 | hib_update | uint32 | 0x78 | 0xd0 01 |
| 27 | hib_ack_update | uint32 | 0x7c | 0xd8 01 |
特性 — C++ 布局不是 proto 字段顺序。字段 1–3 和 5–27 从
0x18到0x7c紧密排列;字段 4(descriptor_source)被拆到0x80。按声明顺序布局 struct,或假设 proto 字段顺序等于内存顺序的重新实现,会从node_id开始误读每个字段。序列化器把字段 4 作为*((int *)this + 32)(即0x80)读取,同时在字段 3(0x18)和字段 5(0x28)wire emission 之间发射 wire tag0x20;proto wire 顺序是正确的,只有内存放置被重排。这是 protobuf 字段 arena packing 的决策(带非零默认值的 enum 与尾部块分组),不是表中的错误。
序列化器中的两次 _WORD 存储(例如字段 16 的 *(_WORD *)a2 = 384)是小端双字节 varint tag 0x80 0x01、0x88 0x01、……,也就是 tag-and-wire-type 字节超过 0x7f 的 proto 字段号 16–27。
默认值与清除
::Clear @0x1cf0eca0 从另一个方向确认了布局。它清零三个 32 字节(ymm)块和一个尾部区域,然后把一个字段设置为非零默认值:
function nf_descriptor_trace_entry::Clear(this): // 0x1cf0eca0
has = *(u32*)(this + 0x10) // hasbits
if (has & 0xFF): zero ymm [this + 0x18] // fields 1,2,3 + below (0x18..0x37)
if (has & 0xFF00): zero ymm [this + 0x38] // 0x38..0x57
if (has & 0xFF0000): zero ymm [this + 0x58] // 0x58..0x77
if (has & 0x7000000): // the trailing block + descriptor_source
*(u64*)(this + 0x78) = 0 // hib_update(0x78), hib_ack_update(0x7c)
*(u32*)(this + 0x80) = 1 // descriptor_source default = BARNA_CORE
*(u32*)(this + 0x10) = 0 // clear hasbits
```text
`*(u32*)(this + 0x80) = 1` 存储是第二个独立证据,说明 `descriptor_source` 位于 `0x80` 且默认值为 `1`(`BARNA_CORE`):序列化器从 `0x80` 读取它,`Clear` 也在那里重置它。
### 嵌套 enum
```text
enum TracePoint: TENSORCORE = 0, BARNACORE = 1, HIB = 2
enum descriptor_source_value: TENSOR_CORE = 0, BARNA_CORE = 1, HIB = 2, HIB_HBM_QUEUE = 3
enum Constants: NUM_LOG_LINES = 2descriptor_source 命名 staging 该描述符的引擎;内存默认值 1(BARNA_CORE)由 Clear 安装。TracePoint id(字段 1)区分 band 内的 trace-event 子类型(TENSORCORE / BARNACORE / HIB)。
2. 三个 SyncFlag-Update 通道
目的
一个 Node-Fabric DMA 在完成时最多可以触发三个独立的 SyncFlag,每个端点角色一个。每个都是 deepsea-descriptor 对 completion semaphore 的类比:源端在不再需要源 buffer 时触发 "buffer free" flag,目标端在远端端点触发 "data arrived" flag,ack 通道触发 "completion" flag。每个通道都由一个 wrapper accessor 暴露,该 accessor 先以 EntryDataCase == 3(cmpl $3, 0x30(rax),即 oneof tag)门控,再以自己的 *_update enable 字段门控,最后打包一个 23 位 SyncFlagTarget。
23 位 SyncFlagTarget pack
三个通道都生成相同的 target 布局:
SyncFlagTarget (23 bits):
bits 0..9 : sync_flag (10 bits, mask 0x3ff)
bit 10 : resource (1 bit)
bit 11 : node (1 bit)
bits 12..22 : chip (11 bits, mask 0x7ff << 12 = 0x7ff000)
```text
最清晰的证据是 `DestinationSyncFlagTarget` @`0xf698340`,它显式打包字段(没有 SIMD fold)。`v2` 是 wrapper 偏移 `+0x28` 处的内部 `nf_descriptor` 消息指针(`*(v1+40)`);门控条件是 `EntryDataCase == 3` 且 `destination_update != 0`:
```c
function DestinationSyncFlagTarget(wrapper): // 0xf698340
msg = wrapper.entry // *(wrapper + 0x10)
if (*(u32*)(msg + 0x30) != 3) return 0 // EntryDataCase != nf_descriptor
nf = *(ptr*)(msg + 0x28) // inner nf_descriptor message
if (nf[21] == 0) return 0 // destination_update (0x54) disabled
return (nf[17] << 12) & 0x7FF000 // destination_chip_id (0x44) -> chip[12:23]
| ((nf[16] & 1) << 11) // destination_node_id (0x40) -> node[11]
| ((nf[23] & 1) << 10) // destination_update_resource (0x5c) -> resource[10]
| (nf[22] & 0x3ff) // destination_update_sync_flag (0x58) -> sync_flag[0:10]五次读取 nf[16]/nf[17]/nf[21]/nf[22]/nf[23] 位于 C++ 偏移 0x40/0x44/0x54/0x58/0x5c,正是 §1 中的 destination_* 字段 12、13、17、18、19。这与表逐字节一致。
SourceSyncFlagTarget @0xf6982e0(门控 source_update@0x60)和 AckSyncFlagTarget @0xf6983a0(门控 ack_update@0x6c)通过共享的 OCI SyncFlag-target fold 达到同一个 23 位布局:用 vpunpcklqdq 将 {node_id, *_update_sync_flag} 交错到一个向量中,用 lane multiplier [1, 0x400, 0x800, 0x1000](fold table @0xa2c2560)执行 vpmulld,再用 [0x3ff, 0x400, 0x800, 0x7ff000](mask @0xa2d5e00)执行 vpand,最后水平 OR。multiplier/mask 对把 lane 0 → sync_flag[0:10]、lane 1 → resource[10]、lane 2 → node[11]、lane 3 → chip[12:23],也就是 Destination 通道显式打包出的同一个 23 位 target。
注意 —
Source/Ackfold 使用{node_id, sync_flag}作为输入向量,因此这两个通道中的 bit-10 "resource" 位是从node_id的低位派生的,而不是来自单独的 resource 字段(Source/Ack 通道把 node-as-resource 编码进去)。Destination 通道读取真实的destination_update_resource字段。这是通道之间的一个小语义不对称;位布局相同(HIGH 置信度 — multiplier/mask 已逐字节解码,但未构造 fold lane 1 等于 Destination 的(resource & 1) << 10的符号证明)。
HIB 通道
不存在第四个 SyncFlagTarget accessor。相反,字段 26/27(hib_update@0x78,hib_ack_update@0x7c)形成 host-interface (HIB) 通道:这两个 bit 选择描述符是否还会触发 HIB 侧 flag 和/或 ack。它们由 jxc-only host-DMA 聚合器(DeriveHostDmaTransfers 的 nf_descriptor_hib 路径)消费,并折叠进 host-DMA timeline。只构建设备侧 fabric 路径的重新实现可以把它们视为不透明 enable bit。
陷阱 — 第四个 wrapper accessor
UpdatedSyncFlagTarget@0xf698400与上面三个并列存在,但它不是nf_descriptor通道。它门控EntryDataCase0xb(brn_sync_wait)/0x9(cs_external_sync_flag_update),属于 TC/BarnaCore sync band,不属于 Node-Fabric descriptor。nf_descriptor正好携带三个 SyncFlag 通道外加 HIB 通道;不要把第四个 accessor 接入此路径。
3. Size 和 Transaction-Id 访问器
另外两个 wrapper accessor 会读取此记录,但不打包 SyncFlag target:
GetDmaSize()@0xf6982a0— 对 case 3(nf_descriptor)返回length(@0x48) << 10,即以 1 KiB 为单位的字节长度。对 case0x13(BMEM nf arm)则返回(@0x2c) << 10。重新实现必须左移 10 才能恢复字节数;描述符存储的是 KiB。GetDmaTransactionId()@0xf698260— 对nf_descriptorcase 返回 0(那里没有 transaction id);它只在 case0x13(BMEM arm)中填充,并打包{((@0x18 bit-0) << 11) | ((@0x28) & 0x700) | ((@0x20) & ~0xfff) | (@0x28 low byte)},即 BMEM transaction id。
陷阱 —
length以 KiB 为单位,不是字节。把length直接当作字节数读取的 DMA-timeline 重新实现会把每个传输都少报 1024 倍。<< 10位于GetDmaSize,不在描述符中。
4. bcs_internal BarnaCore-Sequencer Band
目的
bcs_internal_trace_entry(PerformanceTraceEntry oneof 的 case 15)是 BarnaCore-sequencer 的内部可观测性 band,即 BarnaCore 对 TC cs_internal band 的对应物。它不是 DMA 描述符,但共享同一个 proto family 和同一个 data_field marker/operand sentinel 方案,并且是 BarnaCore 在其驱动的 descriptor staging 周围触发的 band(trace marker、step boundary、scalar fence、host interrupt、sync-trace start/stop)。这里包含它,是因为它的 data_field 携带 DMA band 使用的同一种跨代 marker 编码。
布局
7 字段布局由 _InternalSerialize @0x1cf152a0 和 ::Clear @0x1cf15260 精确到字节:
| proto# | Name | Type | C++ off | Wire tag |
|---|---|---|---|---|
| 1 | id (TracePoint) | enum | 0x30 | 0x08 |
| 2 | tensor_node | uint32 | 0x18 | 0x10 |
| 3 | data_field | uint32 | 0x1c | 0x18 |
| 4 | sync_flag_number | uint32 | 0x20 | 0x20 |
| 5 | program_counter | uint32 | 0x24 | 0x28 |
| 6 | sync_sfence_end | uint32 | 0x28 | 0x30 |
| 7 | sync_sfence_start | uint32 | 0x2c | 0x38 |
hasbits @0x10;_table_ @0x21c56e80。与 nf_descriptor 一样,字段 1(id)位于线外:序列化器把它读作 *((int *)this + 12)(0x30),而字段 2–7 从 0x18 开始连续。::Clear 清零 0x18 处的一个 xmm 块(16 字节,0x18–0x27)以及 0x28 处的一个 qword,然后写入 *(u32*)(this + 0x30) = 122,确认 id 位于 0x30,且默认值 0x7a = 122。
每个 id 的 XLine 映射
6 个 TracePoint id 通过 Component() @0xf697f40(case-15 arm → bcs sub-table @0xab885fc)映射到 BarnaCore device line;名称由 TpuComponentName @0x1c8ebb60 验证:
| id | TracePoint | TpuComponent | XLine name |
|---|---|---|---|
| 122 | BRN_TRACE_INSTRUCTION | 0x3b (59) | "Barna Core Trace Instruction" |
| 123 | BRN_SET_TRACEMARK | 0x3c (60) | "Barna Core Step" |
| 124 | BRN_SYNC_START_STOP_TRACE | 0x16 (22) | "Barna Core Sync Flag" |
| 125 | BRN_HOST_INTERRUPT | 0x3d (61) | "Barna Core Host Interrupt" |
| 126 | BRN_FENCE_START | 0x3e (62) | "Barna Core Fence" |
| 127 | BRN_FENCE_END | 0x3e (62) | "Barna Core Fence" |
Constants: NUM_LOG_LINES = 1。
data_field marker/operand sentinel
bcs_internal 由 V1 路径 ConvertTraceMarksAndInstructionsToXPlane<jxc> @0xf260960 消费,不是由注册的 CoreDispatcher subscriber 消费(不存在 "bcs" subscriber)。data_field 是 cs_internal band 携带的同一个跨代 marker,由三个共享 tracker 解释:
// step boundaries: id 123 (XLine 0x3c "Barna Core Step") via TraceMark() @0xf698140
// StepTracker::ProcessTraceEntry @0xf2c4480, data_field at TraceMarkEntry+0x8:
data_field in {0x7ffffffc, 0x7ffffffd} -> step-boundary sentinel
data_field & ~1 == 0x7ffffffe -> step START / END marker (begin/end)
data_field <= 1 -> dropped (no-op)
2 .. 0x7ffffff9 -> nested step-id push (the step name)
// per-instruction operand: id 122 (XLine 0x3b) via RunTracker::ProcessTraceOperand @0xf2c4460:
if (data_field >= 0xF0000000) run_id = data_field & 0xfffffff // run-boundary marker
else data_field is a per-instruction operand
// overlay open/close (TRACE_INSTRUCTION fan-out) via OverlayTracker::ProcessTraceOperand @0xf2c3e40:
operand kind 0x9 -> overlay close
operand kind 0xd -> overlay open
```text
Scalar fence(id 126/127,XLine `0x3e`)使用 `ScalarFenceStart()` @`0xf698020`(门控 `sync_sfence_start`@`0x2c`)/ `ScalarFenceEnd()` @`0xf698080`(门控 `sync_sfence_end`@`0x28`);fence span 通过 `GetOrCreateLine(0x3e)` 落到 XLine `0x3e`。`BRN_SYNC_START_STOP_TRACE`(124,XLine `0x16`)使用 `SyncFlagValue()` @`0xf6984a0` 读取 `data_field`。id 124/125 的 begin/end-vs-point span 模型未能从 V1 builder 中隔离(LOW 置信度 — 可能是 point event,未逐字节确认)。
---
## 5. deepsea GetDmaId 位编码器
### 目的
DMA-timeline 构建通过一个合成的 **`dma_id`** 复合键,把 begin event 与其匹配的 end event 配对。jxc `Dma` subscriber 以 `FlatHashMap<dma_id>` 为键;deepsea / 现代代际通过 `GetDmaId` 使用同样的配对。证明两个键是相同的逻辑元组,并且现代代际正好按 trace header 的扩宽方式扩宽其组成部分,就闭合了 jxc↔deepsea DMA-timeline 等价性。
### 38 位 deepsea 键
`GetDmaId(int)` @`0xf699ca0` 是 `pxc::profiler::TraceEntry` 上的模板(该单元中唯一的实例化;通过通用 `profiler::TraceEntry` 表面由 vfc/vlc/glc/gfc 共享)。它分派 proto oneof 字段号(`0x96` 个 arm),选择一个 `TraceIdHeader`(int 参数选择最多三个 command header 之一,默认选择 global),并用一种位布局组成 38 位键。组合尾部 @`0xf69a444` 读取 `TraceIdHeader` 字段 `{transaction_id@0x18, core_id@0x1c, chip_id@0x20}`:
```c
// composition tail (0xf69a444); hdr is the selected TraceIdHeader
txid = *(u32*)(hdr + 0x18) // transaction_id
core = *(u32*)(hdr + 0x1c) // core_id
chip = *(u32*)(hdr + 0x20) // chip_id
dma_id = (txid & 0xFF) // -> transaction_id[0:8]
| (txid & 0x1FFF00) // -> transaction_id[8:21]
| ((core & 7) << 21) // -> core_id[21:24]
| ((u64)(chip & 0x3FFF) << 24) // -> chip_id[24:38]
// presence bit dl = 1这在反编译中逐字节精确:单个表达式 v4 = (u32)v3 & 0x1FFF00 | ((v6[7] & 7) << 21) | ((u64)(v6[4] & 0x3FFF) << 24); return v4 | (u8)v3; 其中 v6 是 header 指针,v6[6](=+0x18)=transaction_id,v6[7](=+0x1c)=core_id,v6[4*2](=+0x20)=chip_id。
deepsea dma_id (38 bits):
bits 0..20 : transaction_id (21 bits — per-transfer tag)
bits 21..23 : core_id ( 3 bits — TC0/TC1/SC0..SC3 selector)
bits 24..37 : chip_id (14 bits, mask 0x3fff — the chip in the pod)
```text
6 个 `CmdDmaIdFromEntry<…>` helper @`0xf69a500`–`0xf69a6e0`(用于 `OciCommonReadCmdIssuedFromEngine` / `MemReadReqFromEngine` / `WriteCmdAcceptedAtMn` / `OciWriteCommand` / `OciReadCommand` / `CompletedInTcs`)在使用 int selector 从最多三个 trace_id_header 中选择一个之后,组成相同布局(OCI command 最多携带三个)。`GetDmaId` 只被调用一次,即由 `ConvertTpuTraceToXPlane<pxc>` @`0xf26c8d9` 以 selector `0`(第一个/cmd0 header)调用。
### 与 jxc 键的跨代对应关系
jxc `GetDmaId()` @`0xf698180`(27 位键)从 jxc nf 记录组成相同逻辑元组。对于 `nf_descriptor` arm(case 3),它读取 `{trace_id@0x20, node_id@0x24, chip_id@0x28, descriptor_source@0x80}`,并打包 `(u8)trace_id | (trace_id & 0x1F00) | ((descriptor_source & 3) << 13) | (node_id << 15) | ((chip_id << 16) & 0x7FF0000)`:
```text
jxc dma_id (27 bits): deepsea correspondence:
trace_id[0:13] <-> transaction_id[0:21] (per-transfer tag; widened 13 -> 21)
descriptor_source[13:15] <-> core_id[21:24] (jxc 2-bit engine selector -> deepsea 3-bit core_id)
node_id[15] <-> (folded into core_id) (jxc 1-bit tensor-node selector)
chip_id[16:27] (11b) <-> chip_id[24:38] (14b) (widened 11 -> 14)(jxc descriptor-resource 角色由 descriptor_source 承载,即 staging 描述符的 BarnaCore/TensorCore/HIB 引擎,打包进 bits 13:15 的 2-bit slot;deepsea core_id 是类似的 3-bit core selector。)
特性 — deepsea / 现代代际把
dma_idchip 字段扩宽到 14 位(mask0x3fff),这与 trace header 跨代经历的 chip_id 11/12→14 扩宽相同。每代 proto2chip_id字段都是uint32;14-bit mask 是最大扩宽值,对 pxc(header 中 12-bit chip)来说分配过多,但恰好适配扩宽后的 vfc/vlc/glc/gfc 代际。因此 DMA-pairing key 跟踪的 pod-address 扩宽与 trace header 相同。这证明跨代配对键是同一个逻辑(tag, resource/core, node, chip)元组,只是按代际扩宽。陷阱 — jxc
GetDmaIdsimple arm @0xf69824e(VMEM-ICI / SMEM / IMEM / HIB engine 的 fall-through)进入时eax = 0、ecx = 0,返回dma_id = 0且 presencedl = 0,也就是没有有效 dma_id,会被 subscriber 的 presence test 丢弃。它不会返回id & 0xff。只有 HBM、VMEM_HBM 和 BMEM arm 会生成丰富的复合键。注意 — vfc/glc/gfc 没有逐代
GetDmaId。GetDmaId(int)只是 pxc 模板;现代代际把 DMA band 折叠进 ICI/intra-DMA band(见 Intra-Chip DMA Descriptor)。跨代键已证明是相同元组,但尚未定位到重新内联计算它的现代代际 DMA-end consumer(CONFIRMED-PARTIAL)。
6. 函数映射
| Function | Address | Role |
|---|---|---|
nf_descriptor_trace_entry::_InternalSerialize | 0x1cf0ed20 | 27 字段序列化器(offset/tag 证据) |
nf_descriptor_trace_entry::Clear | 0x1cf0eca0 | 清零 + descriptor_source=1 默认值 |
nf_descriptor_trace_entry::_table_ | 0x21c55ba0 | parse table |
…SourceSyncFlagTarget | 0xf6982e0 | "buffer free" flag(OCI fold) |
…DestinationSyncFlagTarget | 0xf698340 | "data arrived" flag(显式 pack) |
…AckSyncFlagTarget | 0xf6983a0 | "completion" flag(OCI fold) |
…UpdatedSyncFlagTarget | 0xf698400 | 非 nf — brn_sync_wait/cs_external band |
…GetDmaSize | 0xf6982a0 | length << 10(KiB→bytes) |
…GetDmaTransactionId | 0xf698260 | BMEM transaction id(nf 为 0) |
| OCI SyncFlag fold (mul / mask) | 0xa2c2560 / 0xa2d5e00 | [1,0x400,0x800,0x1000] / [0x3ff,0x400,0x800,0x7ff000] |
bcs_internal_trace_entry::_InternalSerialize | 0x1cf152a0 | 7 字段序列化器 |
bcs_internal_trace_entry::Clear | 0x1cf15260 | 清零 + id=122 默认值 |
Component | 0xf697f40 | band→XLine(bcs sub-table @0xab885fc) |
ConvertTraceMarksAndInstructionsToXPlane<jxc> | 0xf260960 | V1 bcs consumer |
jxc GetDmaId | 0xf698180 | 27 位键(jt @0xab88674) |
deepsea GetDmaId(int) | 0xf699ca0 | 38 位键(jt @0xab88c40) |
CmdDmaIdFromEntry<…> × 6 | 0xf69a500–0xf69a6e0 | OCI command-header key helper |
ConvertTpuTraceToXPlane<pxc> | 0xf26c8d9 | 唯一的 GetDmaId(0) caller |
7. 注意事项
- 双字节 tag 的端序。 字段 ≥16 使用小端存储的双字节 varint wire tag(
*(_WORD *)a2 = 384即字节0x80 0x01)。发射这些 tag 的重新实现必须先写低字节。 - KiB 长度单位(§3)是 DMA-timeline 重建中最常见的 off-by-1024 错误来源。
descriptor_source放置(§1)会打破天真的声明顺序 struct 布局;应先布局0x18–0x7c,再把descriptor_source放到0x80。- Multicast/segmented 标志(字段 15/16)会改变目标端点的解释方式(一个 chip vs. fan-out 集合);SyncFlag 通道仍然每个描述符只触发一个 target。
- pxc
GetDmaId是唯一显式 key consumer;现代代际 DMA timeline 把配对折叠进逐代 ICI/intra-DMA 路径(§5 NOTE),因此捕获的 vfc/glc/gfc trace 不会直接执行此函数。
交叉引用
- ICR Node-Fabric DMA — 承载这些描述符的逐引擎 command/data-end 执行的 trace band(case 6)
- Route-Table Generation — 从
destination_chip_id解析描述符多跳路由的逐链路表 - net_router Pipeline — 提供描述符所 staging 的
(src, dst)的逐步调度 - ICI Routing — Section Map — route table、schedule 和 descriptor 如何配合
- Intra-Chip DMA Descriptor — 现代代际把 DMA-pairing band 折叠进去的片上 DMA descriptor