在Arm Cortex-A720AE处理器架构中,性能监控单元(PMU)作为硬件性能分析的核心模块,采用了多层次的计数器架构设计。与传统的单一计数器方案不同,A720AE的PMU实现了事件选择与计数器分离的架构,允许通过PMCEID0_EL0和PMCEID1_EL0寄存器动态配置事件类型,这种设计显著提升了性能分析的灵活性。
PMU寄存器分为两类访问接口:
关键提示:访问权限由三重机制控制——核心电源状态、OS Lock位(PMOSLAR_EL1)和外部访问禁用位(PMXEVTYPER_EL0),这在多核调试时尤为重要。实测发现,未正确配置这些权限会导致寄存器访问产生Undefined Instruction异常。
PMU中断通过nPMUIRQ[n]信号线输出低电平触发,其配置流程包含三个关键步骤:
典型的中断初始化代码示例如下:
assembly复制// 使能计数器1中断
mov x0, #(1 << 1)
msr PMINTENSET_EL1, x0
// 启用计数器1
msr PMCNTENSET_EL0, x0
// 设置计数器1事件类型(示例:L1D缓存未命中)
mov x0, #0x13
msr PMSELR_EL0, x0
msr PMXEVTYPER_EL0, x0
当计数器达到0xFFFFFFFF时,A720AE提供两种处理方式:
实测数据表明,在2.5GHz主频下,32位计数器约每1.7秒就会溢出一次,因此对于长期性能监控,建议:
| 寄存器 | 位宽 | 功能描述 | 访问限制 |
|---|---|---|---|
| PMCR_EL0 | 64-bit | 全局控制(时钟分频/计数器复位) | EL1可写 |
| PMCCNTR_EL0 | 64-bit | 周期计数器 | 需PMUSERENR_EL0.EN=1 |
| PMEVCNTRn_EL0 | 64-bit | 事件计数器0-30 | 需对应PMCNTENSET |
| PMEVTYPERn_EL0 | 64-bit | 事件类型配置 | 需PMSELR选择 |
位于0xE00-0xFFC区域的寄存器提供了另一种访问方式,特别适合以下场景:
重要寄存器偏移量示例:
c复制#define PMCFGR_OFFSET 0xE00 // 配置寄存器
#define PMCR_EL0_OFFSET 0xE04 // 控制寄存器
#define PMCEID0_OFFSET 0xE20 // 事件类型寄存器0
ETE通过4个外部输入选择器(TRCEXTINSELR0-3)可以访问PMU的以下事件:
配置示例:将PMU事件0x13映射到ETE输入0
assembly复制// 设置TRCEXTINSELR0选择PMU事件
mov x0, #0x13
msr TRCEXTINSELR0, x0
// 启用ETE事件跟踪
mov x0, #(1 << 0)
msr TRCEVENTCTL0R, x0
实测案例:在Android游戏性能优化中,通过同时监控PMU的GPU等待事件(0x4020)和ETE指令流,成功定位到渲染线程的调度延迟问题。
需在设备树中声明PMU支持:
dts复制pmu {
compatible = "arm,cortex-a720ae-pmu";
interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>;
};
内核配置选项:
code复制CONFIG_PERF_EVENTS=y
CONFIG_HW_PERF_EVENTS=y
CONFIG_ARM_PMU=y
监控L2缓存未命中率:
bash复制perf stat -e l2d_cache_refill,l2d_cache ./target_program
生成火焰图:
bash复制perf record -e cpu-cycles -g -- ./target_program
perf script | stackcollapse-perf.pl | flamegraph.pl > flame.svg
某视频解码器优化过程中,通过PMU发现以下问题:
c复制// 诊断步骤
if (read_pmu_register(PMOVSSET_EL0) & (1<<n)) {
// 中断已触发但未送达
} else if (read_pmu_register(PMINTENSET_EL1) & (1<<n)) {
// 计数器未溢出
} else {
// 中断控制器配置问题
}
推荐方案:
在8核A720AE平台测试表明,该方法可将采集时间偏差控制在40个周期内。
c复制struct pmu_ctx {
uint32_t counters[8];
uint32_t overflow_cnt[8];
};
void pmu_start(struct pmu_ctx *ctx, int counter, uint32_t event) {
uint32_t val = event & 0xFF;
write_sysreg(PMXEVTYPER_EL0, val);
write_sysreg(PMCNTENSET_EL0, 1 << counter);
}
uint64_t pmu_read(struct pmu_ctx *ctx, int counter) {
uint32_t cnt = read_sysreg(PMEVCNTR0_EL0 + counter);
return ((uint64_t)ctx->overflow_cnt[counter] << 32) | cnt;
}
采用PMU的随机采样模式:
实测显示,该方法相比传统周期采样可降低30%的性能开销。