Cortex-M85处理器的性能监控单元(PMU)是其调试与性能分析子系统的核心组件。作为Armv8-M架构中的标准配置,该PMU实现了两类事件监控能力:
M85的PMU包含8个32位事件计数器(PMU_EVCNTR0-7)和1个独立的周期计数器(PMU_CCNTR)。每个事件计数器可通过PMU_EVTYPER寄存器配置为监控特定事件,其关键特性包括:
重要提示:使用PMU前必须设置DEMCR.TRCENA=1来启用调试组件,否则所有计数器将保持禁用状态。
M85的PMU事件可划分为以下几大类:
| 事件类别 | 典型事件 | 应用场景 |
|---|---|---|
| 指令执行 | INST_RETIRED, EXC_TAKEN | 代码覆盖率分析 |
| 内存访问 | L1D_CACHE_REFILL, DTCM_ACCESS | 内存子系统优化 |
| 分支预测 | BR_MIS_PRED, BR_PRED | 分支密集型代码优化 |
| 流水线停滞 | STALL_FRONTEND, STALL_BACKEND | 指令调度优化 |
| 总线活动 | BUS_ACCESS, AXI_READ_ACCESS | 总线带宽分析 |
| 安全扩展 | SE_CALL_S, SE_CALL_NS | 安全状态切换监控 |
| MVE向量指令 | MVE_INST_RETIRED | DSP算法性能分析 |
M85的L1缓存事件对性能调优至关重要:
c复制// 典型缓存性能分析代码示例
void cache_analysis() {
enable_counter(0, L1D_CACHE_REFILL); // L1D缓存行填充
enable_counter(1, L1D_CACHE); // L1D缓存访问
enable_counter(2, LL_CACHE_MISS_RD); // 缓存读未命中
start_counters();
critical_section();
stop_counters();
float miss_rate = (float)get_count(0) / get_count(1);
printf("L1D缓存未命中率: %.2f%%\n", miss_rate*100);
}
关键缓存事件说明:
分支预测失败会导致10-20个周期的流水线刷新,相关事件包括:
优化建议:
针对DSP应用的专用事件组:
assembly复制; MVE指令性能分析示例
vmul.f32 q0, q1, q2 ; 触发MVE_FP_RETIRED
vldrw.u32 q3, [r0] ; 触发MVE_LD_RETIRED
vstrw.u32 q4, [r1] ; 触发MVE_ST_RETIRED
关键MVE事件:
c复制void setup_pmu_event(uint8_t counter_num, uint32_t event_id) {
// 1. 选择计数器并设置事件类型
PMU->PMU_EVTYPER[counter_num] = event_id & 0xFFFF;
// 2. 清除计数器初始值
PMU->PMU_EVCNTR[counter_num] = 0;
// 3. 启用计数器
PMU->PMU_CNTENSET = 1 << counter_num;
// 4. 全局启用PMU
PMU->PMU_CTRL |= PMU_CTRL_ENABLE_Msk;
}
场景:优化图像处理流水线
配置计数器组:
分析指标:
优化方向:
通过PMU_EVTYPER寄存器可设置事件过滤条件:
c复制// 只监控用户模式下的缓存未命中
PMU->PMU_EVTYPER[0] = L1D_CACHE_REFILL | PMU_EVTYPER_UEN_Msk;
可用过滤标志:
c复制// 设置Counter0在溢出时触发中断
PMU->PMU_INTENSET = (1 << 0); // 使能中断
NVIC_EnableIRQ(PMU_IRQn); // 启用NVIC通道
void PMU_IRQHandler(void) {
uint32_t flags = PMU->PMU_OVSSET;
if (flags & (1 << 0)) {
printf("Counter0 overflow detected!\n");
PMU->PMU_OVSCLR = (1 << 0); // 清除标志
}
}
通过设置ACTLR.EVENTBUSEN可将PMU事件实时输出到EVENTBUS信号:
原始代码问题:
优化措施:
优化结果:
监控组合:
发现:
解决方案:
排查步骤:
可能原因:
在异构系统中:
c复制#pragma monitor=PMU_Reset
void PMU_Reset(void) {
PMU->PMU_CNTENCLR = 0xFF; // 禁用所有计数器
PMU->PMU_OVSCLR = 0xFF; // 清除溢出标志
}
使用pyOCD脚本示例:
python复制from pyocd.probe.pydapaccess import DAPAccess
dap = DAPAccess.get_device(0)
dap.write_mem(0xE000EDFC, [0x01000000]) # DEMCR.TRCENA
基准测试流程:
事件选择策略:
低开销监控技巧:
安全关键系统注意事项: