v7x Perf-Counters
本页中的所有地址均适用于来自
libtpu-0.0.40-cp314wheel 的libtpu.so(build-id89edbbe81c5b328a958fe628a9f2207d)。.text、.rodata和.lrodatasection 映射满足 VMA == 文件偏移;kDeviceTypeInfo位于 VMA0x1c60480。其他 build 会有所不同。
摘要
TPU profiler 暴露两层不同的设备端 telemetry,它们共享同一个后备结构:kDeviceTypeInfo 数组(17 项,stride 0x448 = 1096 字节,每个 DeviceType 一项)。第一层是具名硬件性能计数器解析器:GetPerformanceCounterNames<N>(0xf240980 / 0xf240c00 / 0xf240ac0),它把用户选择的 counter ordinal 转成 profiler 标到 counter timeline 上的片上寄存器名称。第二层是 firmware power/thermal/DVFS 事件模型:ConvertFirmwareTraceEntriesToXPlane 输入 FirmwareEventBuilder,把 firmware trace 转换为 power、temperature、throttle 和 P-state XStats。两者都从同一条 kDeviceTypeInfo 行读取各代常量。
perf-counter 层最重要的事实是:除一个 silicon generation 外,它在所有 generation 上都是惰性的。三个 resolver 实例化开头都是 if (DeviceType == 12),否则立即返回。DeviceType 12 是 v7x,它是不同于 v6e(DeviceType 13)的独立 silicon generation;这同时由 enum 名称字符串(.rodata 中存在 v7x、v6e、Viperfish)和 descriptor 字节确认:只有 0x1c637e0 行(index 12)带有非零 perf-counter enum base;其他每一行在六个 descriptor 字段上都是全零。因此,“v7x” 不是叠加在旧 silicon 上的 counter-naming 轴,而是此 build 中唯一暴露具名设备端 counter 的 generation。counter-name dispatcher 本身位于 asic_sw::driver::deepsea::gxc::gfc::profiler namespace;gfc 是 gxc namespace profiler codec family 之一(glc 的 sibling),v7x 通过它解析 counter 名称(本页在 § Codec Family 中建立该关系)。
六个 perf-counter set 与六个 STATS_COUNTER_SAMPLE_ISSUED_FROM_{TCS,SCS,SCTC,SCTD,CMNUR,ICR} tracepoint source 1:1 对应:TensorCore Sequencer、SparseCore Scalar、SparseCore Tile Compute、SparseCore Tile DMA、memory-network/HBM controller,以及 ICI router。tracepoint 携带采样到的 counter 值;此 resolver 提供它们的名称。本页涵盖:resolver 及其 base + index*8 enum 编码;六个 kDeviceTypeInfo descriptor 字段和恢复出的 counter-name 字符串;请求驱动的 counter-index 选择(GetTpuCounterIndicesFromRequest);以及 firmware FirmwareEventBuilder 事件模型和它的各代 calibration bundle。
对于重新实现,契约是:
DeviceType == 12gate,以及每个 resolver 使用的PerformanceCounterName = base + ordinal*8enum 编码。- 六个
kDeviceTypeInfodescriptor 字段(+0x2c8/+0x348/+0x350/+0x358/+0x438/+0x440)是打包的 enum base(不是 bit-mask),每个 counter set 一个,并带有 resolver inline-cap(28/28/28/28/3/12)。 GetTpuCounterIndicesFromRequest中的 request→set 连接:哪个XprofRequestflag bit 选择哪个TpuCounterIndicesmember,并输入哪个 resolver。- firmware 事件模型:五个
TraceEntryoneof 变体、RunLengthTracker聚合、power/temperature/throttle/P-state 公式,以及各代+0x360..+0x398calibration bundle。
Resolver <28> | xprof::tpu::GetPerformanceCounterNames<28> @ 0xf240980 |
Resolver <12> | ...<12> @ 0xf240c00(ICR set) |
Resolver <3> | ...<3> @ 0xf240ac0(CMNUR/HBM set) |
| 名称 dispatcher | asic_sw::driver::deepsea::gxc::gfc::profiler::PerformanceCounterNameToString @ 0x1fc701e0(20 个 band 子 decoder 0x1fc703e0..) |
| 请求 decoder | xprof::tpu::GetTpuCounterIndicesFromRequest @ 0xf2c5000 |
| 调用方 | ConvertTpuTraceToXPlaneV2<...jxc::PerformanceTraceEntry>(六个 call site) |
| Firmware convert | ConvertFirmwareTraceEntriesToXPlane<...gfc::TraceEntry> @ 0xf2b1ee0(vlc 0xf27f140,glc 0xf29d5c0,vfc 0xf28ae00) |
| Firmware builder ctor | FirmwareEventBuilder<...gfc::TraceEntry>::FirmwareEventBuilder @ 0xf2b8780(vlc 0xf284d20) |
kDeviceTypeInfo | 0x1c60480,17 × 0x448;v7x 行(DT12)@ 0x1c637e0 |
| 活跃 generation | DeviceType == 12 = 仅 v7x;其他所有行全零 |
v7x Gate 与 Counter-Name Resolver
用途
GetPerformanceCounterNames<N> 是命名设备端 HW performance counter 的函数。给定用户选择的 counter ordinal 列表和一个按 set 的 enum base,它会为每个 ordinal 生成一个 string_view 名称。这些名称成为 v7x counter timeline 上的列标签。它有三个实例化,区别仅在 InlinedVector inline capacity N,即该 set 可承载的最大 counter 数:<28> 用于四个 TensorCore/SparseCore set,<12> 用于 ICI router set,<3> 用于 HBM/memory-network set。
入口点
ConvertTpuTraceToXPlaneV2<jxc::PerformanceTraceEntry> ── six call sites, one per counter set
└─ GetPerformanceCounterNames<28|12|3> (0xf240980 / 0xf240c00 / 0xf240ac0)
└─ gxc::gfc::profiler::PerformanceCounterNameToString (0x1fc701e0)
└─ ToString0..19 (0x1fc703e0 ..) ── 20 banded binary-search sub-decoders算法
三个 resolver 共享同一函数体。这里复现 <28> 形式;<12> 和 <3> 只在 inline-capacity 常量上不同。
function GetPerformanceCounterNames_28(DeviceType d, // 0xf240980
const int* idxs, size_t n,
uint64_t base, // the kDeviceTypeInfo field
InlinedVector<string_view,28>* out):
if d != 12: // 0xf240980 — gate: ONLY v7x; every other gen returns empty
return
reserve(out, n) // grow inline storage if n > current cap
for i in 0 .. n-1: // loop @ 0xf240a20
ordinal = idxs[i] // movsxd from the request-selected index vector
name = PerformanceCounterNameToString(base + 8 * ordinal) // 0xf240a28
out.push_back(name) // EmplaceBackSlow when storage is full算术 base + 8*ordinal 就是完整编码(call site 0xf240a24 处的 lea rdi,[r14 + rax*8],其中 r14 持有按 set 的 base)。PerformanceCounterName 是打包的 32 位 enum;按 set 的 base 选择 counter family(chiplet / unit / domain),ordinal 则以 8 为缩放索引其中的 counter。
QUIRK —
<N>template 参数是InlinedVectorinline capacity,即按 set 的最大 counter 数;不是 counter ID,也不是 device generation。<28>承载最多 28 个名称,<12>最多 12 个,<3>最多 3 个。把N当成其他含义的重新实现会错误设置四个 SparseCore/TensorCore set 的大小。GOTCHA —
DeviceType == 12gate 是完整的 generation guard。没有 fallback 路径:在 v6e、v5p、v5e、v4、v3、v2 上,resolver 返回空 vector,counter timeline 不带名称。若重新实现省略该 gate,或在非 v7xkDeviceTypeInfo行(base 为零)上驱动 resolver,就会解引用PerformanceCounterNameToString(0 + 8*ordinal),并生成垃圾 / 空字符串,而不是预期的 no-op。
六个 Descriptor 字段
六个 kDeviceTypeInfo 尾部字段是打包的 32 位 PerformanceCounterName enum base,每个 counter set 一个。它们存储在低 32 位,高 32 位为零;在 v7x(DT12,行 0x1c637e0)上逐字节精确读出为:
| kDTI 字段 | Resolver | Inline cap | 子系统(STATS_COUNTER source) | v7x base |
|---|---|---|---|---|
+0x2c8 | <28> | 28 | TCS — TensorCore Sequencer | 0xa668a008 |
+0x348 | <28> | 28 | SCS — SparseCore Scalar | 0xa7f61008 |
+0x350 | <28> | 28 | SCTC — SparseCore Tile Compute | 0xa7724008 |
+0x358 | <28> | 28 | SCTD — SparseCore Tile DMA / TEC | 0xa6726008 |
+0x440 | <3> | 3 | CMNUR — memory-network / HBM controller | 0xa5463408 |
+0x438 | <12> | 12 | ICR — ICI router(data) | 0xd6438c08 |
NOTE — 这六个字段不是 bit-mask。每个字段都是打包的
PerformanceCounterNameenum base,会与ordinal*8相加。resolver 函数体中的base + ordinal*8算术(lea rdi,[r14+rax*8])直接证明该字段是加法 base,不是 mask。共有六个 counter set,包括+0x440→<3>CMNUR 和+0x438→<12>ICR。
其他每个 DeviceType 行在这六个字段上都是 0x00000000;扫描全部 17 行验证只有 index 12(0x1c637e0)非零。因此,整个具名 counter 机制在此 build 中是 v7x 专属的。
恢复出的 Counter 名称
实际 counter 字符串位于通过 20 个 band 子 decoder 到达的 gfc 名称表(SamplingData::kNames / kOffsets)中。对每个 set 的前几个 ordinal 解析 base + ordinal*8,可逐字节精确恢复名称(全部确认存在于 .rodata);完整寄存器前缀是 v7x 复用的 VF_(Viperfish-register)namespace:
| Set | base | ordinal 0..3(..._UNPRIVILEGED_ 之后的 suffix) |
|---|---|---|
SCS(+0x348) | 0xa7f61008 | COUNT_CYCLES / COUNT_SCALAR_ISSUE / COUNT_BRANCH_TAKEN / COUNT_S0_INSTRUCTION |
SCTC(+0x350) | 0xa7724008 | COUNT_CYCLES / COUNT_VECTOR_ISSUE / COUNT_V0_INSTRUCTION / COUNT_V1_INSTRUCTION |
SCTD(+0x358) | 0xa6726008 | COUNT_CYCLES / TEC_SCALAR_ISSUE / TEC_BRANCH_TAKEN / TEC_S0_INSTRUCTION |
CMNUR(+0x440) | 0xa5463408 | CYCLE_COUNTER_WINDOW / RD_RSP_BEAT_FROM_HBM / WR_REQ_BEAT_TO_HBM |
ICR(+0x438) | 0xd6438c08 | LINK0_EGRESS_CONTROL_PACKET_SENT / LINK0_EGRESS_DATA_PACKET_SENT / LINK0_INGRESS_CONTROL_PACKET_RECEIVED / LINK0_INGRESS_DATA_PACKET_RECEIVED |
TCS(+0x2c8) | 0xa668a008 | (未命名 — 见 GOTCHA) |
示例完整名称(已在二进制中逐字确认):
VF_CHIP_DIE0_SC_0_SCS_SC_STATS_COUNTERS_UNPRIVILEGED_COUNT_CYCLES
VF_CHIP_DIE0_CMN_CMNUR_0_CMN_STATS_DEBUG_FIXED_STATS_COUNTERS_UNPRIVILEGED_RD_RSP_BEAT_FROM_HBM
VF_CHIP_CHIPLET_ICR_ICR_DATA_0_DEBUG_DOMAIN_ICR_DATA_STATS_PACKET_COUNTERS_UNPRIVILEGED_LINK0_EGRESS_DATA_PACKET_SENTGOTCHA —
+0x2c8处的 TCS(TensorCore)set 经PerformanceCounterNameToString解析到一个gfcdispatcher 不映射的 enum band;ordinal 0..N 在那里返回空。该 set 确实是 TCS source(与STATS_COUNTER_SAMPLE_ISSUED_FROM_TCS和0x10000000request flag 1:1 对应),并使用<28>resolver,但 TensorCore counter 字符串位于 decoded here 的gfcband chain 未到达的独立 TC-specific name table 中。重新实现者必须定位该 TC table,才能命名 v7x TensorCore counter;其他五个 set 是完整的。
Codec Family(gfc)与 v7x Gate {#codec-family-gfc-vs-the-v7x-gate}
名称 dispatcher 是 asic_sw::driver::deepsea::gxc::gfc::profiler::PerformanceCounterNameToString。gfc 是 gxc namespace 中的 profiler codec family(与 glc 同为 sibling;vlc/vfc 位于 vxc 下)。resolver gate 是 DeviceType == 12 = v7x,但通过 gfc 表解析名称。这是一致的,并不矛盾:v7x 是比 v6e(DeviceType 13)更新的 silicon generation,但它共享 gfc profiler codec 和 Viperfish(VF_)寄存器 namespace。firmware 路径作出相同选择:FirmwareEventBuilder 和 ConvertFirmwareTraceEntriesToXPlane 实例化在 gfc::profiler::TraceEntry codec 上(并列存在 vlc、glc、vfc sibling)。因此 v7x/gfc 的关系是:v7x 是由 gfc codec 服务的一个独立 DeviceType,enum base 和 calibration 常量按 kDeviceTypeInfo 行变化。
请求驱动的 Counter 选择
用途
交给 resolver 的 counter-index vector 不是硬编码在二进制中的,而是由 profiling request 通过用户选择。GetTpuCounterIndicesFromRequest(0xf2c5000)把 XprofRequest.PeriodicCounterSamplingOptions repeated-int proto 字段复制到一个 TpuCounterIndices struct 的六个 InlinedVector<int,N> member 中,由五个 flag bit 加一个 byte flag 控制。
算法
function GetTpuCounterIndicesFromRequest(XprofRequest* req, // 0xf2c5000
TpuCounterIndices* out):
flags = req->u32[+0x10]
if flags & 0x10000000: Assign<28>(out + 0x000, req->sampling_opts[+0xb8]) // TCS
if flags & 0x20000000: Assign<28>(out + 0x078, req->sampling_opts[+0xc0]) // SCS
if flags & 0x40000000: Assign<28>(out + 0x0f0, req->sampling_opts[+0xc8]) // SCTC
if flags & 0x00000020: Assign<28>(out + 0x168, req->sampling_opts[+0x48]) // SCTD
if (int)flags < 0: Assign<3> (out + 0x1e0, req->sampling_opts[+0xd0]) // CMNUR (sign bit = 0x80000000)
if req->u8[+0x14] & 1: Assign<12>(out + 0x1f8, req->sampling_opts[+0xd8]) // ICRAssign<N> 是 absl::inlined_vector_internal::Storage<int,N>::Assign<...>,它把 repeated-int proto 字段(一个 RepeatedIterator<int const> range)复制到 member 的 inline storage 中。每个 member 的 <N> 与 resolver cap 完全匹配:四个 SparseCore/TensorCore member 是 28/28/28/28,CMNUR 是 3,ICR 是 12。
Request → Set 连接
| Flag bit | TpuCounterIndices member | Assign<N> | 输入的 Resolver | kDTI base | Set |
|---|---|---|---|---|---|
+0x10 & 0x10000000 | +0x000 | <28> | <28> | +0x2c8 | TCS |
+0x10 & 0x20000000 | +0x078 | <28> | <28> | +0x348 | SCS |
+0x10 & 0x40000000 | +0x0f0 | <28> | <28> | +0x350 | SCTC |
+0x10 & 0x00000020 | +0x168 | <28> | <28> | +0x358 | SCTD |
+0x10 & 0x80000000(sign) | +0x1e0 | <3> | <3> | +0x440 | CMNUR |
+0x14 & 0x1 | +0x1f8 | <12> | <12> | +0x438 | ICR |
NOTE — 第一个 member 位于 struct offset
+0x000,不是+0x8。反编译出的Assigntarget 是裸 struct pointer,因此六个 member 位于0x000 / 0x078 / 0x0f0 / 0x168 / 0x1e0 / 0x1f8。下游调用方(ConvertTpuTraceToXPlaneV2)从这些 offset 读取每个InlinedVector的.data(),并在调用 resolver 前把它与匹配的 kDTI base 配对。
随后,六个填充后的 vector 会在 ConvertTpuTraceToXPlaneV2 中逐一交给六次 GetPerformanceCounterNames 调用。因而 request flag 决定收集哪些 counter;resolver 决定它们叫什么。
Firmware Telemetry 事件模型
用途
ConvertFirmwareTraceEntriesToXPlane<fam> 把 firmware trace 转换为 power / temperature / throttle / P-state 事件。它从 kDeviceTypeInfo 行加载按 generation 的 calibration bundle,构造 FirmwareEventBuilder<fam>,对 trace 做 run-length 聚合,并写回 DVFS P-state timeline。它按 codec family 实例化:gfc(0xf2b1ee0)、vlc(0xf27f140)、glc(0xf29d5c0)、vfc(0xf28ae00),形状相同。
入口点
ConvertFirmwareTraceEntriesToXPlane<gfc::TraceEntry> (0xf2b1ee0)
├─ load [kDTI + DeviceType*0x448 + 0x360..+0x378] ── 4 doubles (power/thermal calibration)
├─ load [kDTI + DeviceType*0x448 + 0x380..+0x398] ── 4 ulongs (meter / sensor / window counts)
├─ FirmwareEventBuilder<gfc>::ctor (0xf2b8780) ── caches 19 XLines + 5 XStatMetadata
├─ RunLengthTracker<double,...>::ProcessTraceEntry ── per entry: Get() value, close runs on change
├─ FirmwareEventBuilder::Flush ── close the trailing run
└─ assign vector<pair<u64, PState>>& (out arg) ── the DVFS timeline算法
function ConvertFirmwareTraceEntriesToXPlane_gfc(entries, gtc_span, // 0xf2b1ee0
DeviceType d,
vector<pair<u64,PState>>& dvfs_out,
TpuXPlaneBuilder* xb):
if d >= 17: trap // 0x448-stride bound check
row = kDeviceTypeInfo + 1096 * d // 1096 == 0x448
doubles = { row[+0x360], row[+0x368], row[+0x370], row[+0x378] } // bias/scale pair per rail
ulongs = { row[+0x380], row[+0x388], row[+0x390], row[+0x398] } // meter/sensor/window counts
fb = FirmwareEventBuilder_gfc(xb, doubles..., ulongs...) // 0xf2b8780
for e in entries:
RunLengthTracker.ProcessTraceEntry(fb, e) // Get() -> value; OnEvent() closes prev run
fb.Flush()
dvfs_out = fb.dvfs_timeline // __assign_with_size of pair<u64,PState> vectorBuilder 状态
FirmwareEventBuilder ctor(0xf2b8780)布局为:
| obj offset | 来源 | 含义 |
|---|---|---|
+0x00 | xb | TpuXPlaneBuilder* |
+0x08 / +0x0c | xb[+0x8c] / xb[+0x90] | core / chip id |
+0x10 / +0x18 / +0x20 / +0x28 | kDTI +0x360..+0x378 | 4 个 calibration double(bias/scale) |
+0x30 / +0x38 / +0x40 / +0x48 | kDTI +0x380..+0x398 | 4 个 calibration ulong(counts) |
+0x60 / +0x58 | 19 | 19 项 TpuComponent vector 大小 |
+0x80 | "power"(id 5) | XStatMetadata |
+0x88 | "temperature"(id 11) | XStatMetadata |
+0x90 | "throttle %"(id 10) | XStatMetadata |
+0x98 | "P State"(id 7) | XStatMetadata |
+0xa0 | "PCIe BW (GB/s)"(id 14) | XStatMetadata |
19 个 firmware XLine component 从 ymmword_AB59ACC(0xab59acc 处的 int32[19])逐字节读取:{120,121,122,123,124,125,126,127,128,129,130,134,135,136,137,138,139,141,143}。按 TpuComponentName,它们是 VDD-core power meters PL1..PL4、VDD-core throttle、HBM power meters PL1..PL4、HBM throttle、HBM max temperature、PCIe read utilization 1..4、PCIe write utilization 1..2、ICR Stats,以及 Compute-Die max temperature;均已作为 .rodata 字符串确认(例如 "VDD Core FW Power Meter PL1(W)"、"Compute Die FW Max Temperature(C)")。
五个事件 Family
firmware TraceEntry 是 oneof;FirmwareEventBuilder::Get(0xf2b8b00)按变体分发,并使用 obj 中存储的 calibration 常量计算每个事件的值:
| 变体(proto message) | Get() 值公式 | XLine / XStat |
|---|---|---|
PowerLevelEvent | power(W) = (raw_energy/Δt − bias) / (meter_count·1000.0),由 obj +0x10/+0x18 缩放并由 +0x20/+0x28 加偏置 | 120..128 / "power";PCIe 134..139 / "PCIe BW (GB/s)" |
ThermalEvent | temperature(C) = cvtsi2sd(thermal_sensor_int) | 130/143 / "temperature" |
ThrottleEvent | throttle% = (throttle_cycles·100.0) / cycle_window | 124/129 / "throttle %" |
DvfsEvent | P-state = cvttsd2si(dvfs_p_state);push pair<u64 ts, PState> | DVFS timeline / "P State" |
MgrFwEvent(tag 94) | manager-firmware status passthrough | mgr line |
常量固定在 .rodata 中:qword_A2DF5C0 处的 100.0,qword_A2E0430 处的 1000.0,qword_A2DF230 处的 1.0,以及 xmmword_A2C1520 / xmmword_A2C5F90 处的 u64→double 2^52 重建 magic。PowerLevel 路径使用 obj +0x10/+0x18 作为按 rail 的 scale,+0x20/+0x28 作为 bias(两个 rail;v35 == 1 和 v35 == 2 分支选择哪个 double 对)。
NOTE — 精确的
power_level_index → power-meter-line路由(PL1..PL4 中哪个映射到哪个kDeviceTypeInfopower-meter instance、VDD-core rail 还是 HBM rail)是从OnEventcomponent-index 选择中结构化解码出来的,但未逐 index 枚举。19-line 集、5 个 stat 和 power 公式已固定;OnEvent内每个power_level_index的 line assignment 是 firmware 路径中的一个中等置信度缺口。
RunLengthTracker 和 OnEvent
RunLengthTracker<double, FirmwareEventBuilder, ...>::ProcessTraceEntry 是聚合引擎。对每个 trace entry,它调用 Get() 提取当前 scalar;当值变化(run-length 边界)时,它调用 OnEvent(span=[prev_gtc, cur_gtc], prevEntry, curEntry, value),把上一个 run 关闭为单个 duration XEvent,然后记录新 run。Flush 关闭尾部 run。
OnEvent(gfc 中 0xf2b8d20)对每个事件:从变体选择 component index,GetOrCreateLine(TpuComponent),AddEvent(GtcSpan, XEventMetadata),也就是写入通用 device_offset_ps / device_duration_ps stat,然后用匹配的 interned stat("power" / "temperature" / "throttle %",位于 obj +0x80..+0x90)调用 AddStatValue<double&> 加上数值。DvfsEvent 路径把一个 pair<u64 timestamp, PState> push_back 到 obj +0xf0 的 timeline vector。
DVFS Timeline
DvfsEvent 流构建一个 vector<pair<u64 gtc, PState>>,即 convert 函数的输出引用参数,通过 __assign_with_size 赋值。PState 是 xprof enum {P_STATE_ACTIVE, P_STATE_INACTIVE, PSTATE_REQUEST_RECEIVED, PSTATE_REQUEST_COMPLETED, PSTATE_REQUEST_DROPPED}(五个字符串都已在 .rodata 中确认)。随后该 timeline 作为 Span<pair<u64,PState>> 传给 compute-trace converter,用芯片在各点观测到的 P-state 标注逐 instruction compute trace,即 DVFS overlay。它与 kDeviceTypeInfo 其他位置的静态 DVFS frequency ladder 闭环:ladder 是 operating point,DvfsEvent 流是观测到的逐 run P-state transition。
各代 Calibration Bundle
+0x360..+0x398 bundle 按 DeviceType 存放,从 kDeviceTypeInfo 逐字节读取:
| Gen(row) | +0x360 | +0x368 | +0x370 | +0x378 | +0x380 | +0x388 | +0x390 | +0x398 |
|---|---|---|---|---|---|---|---|---|
v7x(idx 12, 0x1c637e0) | 0.2 | 0.0 | 0.2 | 0.0 | 0 | 30 | 26 | 50 |
v6e(idx 13, 0x1c63c28) | 0.785 | 9.04 | 0.887 | 0.225 | 0 | 31 | 11 | 50 |
| (idx 10, 11) | 0 | 0 | 0 | 0 | 0 | 31 | 0 | 0 |
| (其他行) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
四个 double 是 power/thermal calibration coefficient(按 rail 的 energy→W bias/scale):v7x 使用平坦的 0.2 对,v6e 使用更丰富的 0.785 / 9.04 / 0.887 / 0.225 曲线。四个 ulong 是 Get() 中作为 divisor / range 使用的各代 count:+0x388 = power meter 数量(v7x 上 30,v6e 上 31),+0x390 = thermal/throttle item 数量(v7x 上 26,v6e 上 11),+0x398 = sample/window count(50)。
NOTE — 与 perf-counter descriptor(仅 v7x)不同,firmware calibration bundle 在 v7x 和 v6e 上都有填充,另有两行只带有
+0x388 = 31。因此 firmware power/thermal/DVFS 层不是 v7x 专属;只有具名 HW perf-counter 层是。v6e 四系数曲线究竟是 polynomial(a0 + a1·x + …)还是独立的按 rail coefficient 尚未完全分离;Get()PowerLevel 数学使用+0x10/+0x18作为 bias/scale,让高阶 v6e 项(9.04、0.887)的角色保持低置信度。
尚未追踪
- TCS / TensorCore counter 名称。
+0x2c8base0xa668a008 + ordinal*8落入一个gfcPerformanceCounterNameToString不映射的PerformanceCounterNameband(对所有已采样 ordinal 返回空)。该 set 已识别(TCS、<28>、0x10000000flag),但其名称表在别处。 - 完整 enum bit-layout。
base + ordinal*8证明 counter ordinal 是低 bitfield(×8);高位的分区(哪些 bit = chiplet / die / unit instance,0xa5/0xa6/0xa7/0xd6高 nibble 作为 subsystem domain 的含义)尚未 bit-slice。可通过扫描 20 个ToStringband 恢复。