1. AMU簡介
AMU是ActivityMonitor Unit的縮寫,在Arm v8.4架構(gòu)中引入。從Arm文檔描述來看,AMU與PMU(PerformanceMonitor Unit)有類似的性能監(jiān)控功能,但其設(shè)計(jì)初衷是為了系統(tǒng)管理,而PMU的用途是用戶態(tài)程序或者調(diào)試功能。AMU可以為系統(tǒng)性能、功耗管理提供持續(xù)不斷的監(jiān)控并獲取非常有用的信息。
在介紹AMU之前,先介紹下PMU幾個(gè)基本概念,對了解AMU會有所幫助。PMU用于跟蹤、統(tǒng)計(jì)系統(tǒng)內(nèi)部的一些底層硬件事件,這些事件反映了程序在CPU上執(zhí)行的行為,可以幫助我們對程序進(jìn)行分析和調(diào)優(yōu):
Event,即事件,例如CPU相關(guān)的事件包括執(zhí)行指令數(shù),時(shí)鐘周期等,cache相關(guān)的事件包括各級cache訪問、refill計(jì)數(shù)等,以及與TLB有關(guān)的事件等。每個(gè)event都有一個(gè)特定的eventid。
Counter,事件計(jì)數(shù)器,數(shù)量固定,以Cortex-A53為例,一共有1+6個(gè)Counter,CycleCounter只用于記錄CPU Cycle數(shù),另外6個(gè)Counter是可配置的Counter,根據(jù)配置選擇的Event進(jìn)行計(jì)數(shù)。當(dāng)計(jì)數(shù)器發(fā)生溢出時(shí),計(jì)數(shù)器會產(chǎn)生overflow中斷。

AMUv1架構(gòu)定義(architecturally defined)4個(gè)固定的EventCounter,分別是:

既然是架構(gòu)規(guī)定要實(shí)現(xiàn)的,那么一定是被經(jīng)常用到的Event。未來可能會增加固定Counter的數(shù)量,AMU最多可支持16個(gè)固定Counter。
除了固定Counter,還支持最多16個(gè)輔助的Counter,這些Counter可以做成固定的也可以是可配置的,取決于具體實(shí)現(xiàn)。所有AMUEvent Counter都是64位的,在溢出時(shí)沒有提示或者中斷。
2. AMU寄存器
AMU寄存器如下圖所列,

AMU寄存器通過systemregister接口MRS和MSR指令進(jìn)行訪問。
訪問控制
可通過配置控制是否允許更低EL級別訪問AMU寄存器。AMUSERENR_EL0.ENbit控制EL0訪問AMU寄存器,可在EL1,EL2,EL3中配置。CPTR_EL2.TAMbit控制來自EL0和EL1的訪問,CPTR_EL3.TAMbit控制來自EL0,EL1和EL2的訪問。其他的配置,只允許在最高的EL級別中進(jìn)行。
Counter寄存器

Counter寄存器都有以下特點(diǎn):
都是64位寄存器,并且overflow時(shí)不產(chǎn)生狀態(tài)提示或者中斷;
CPU復(fù)位后Counter寄存器值恢復(fù)為0;
Event Type寄存器

EventType寄存器配置了各Counter對哪個(gè)event進(jìn)行計(jì)數(shù),這點(diǎn)與PMU的PMEVTYPER
AMCNTENSET0_EL0/AMCNTENCLR0_EL0可對各Counterenable/disable進(jìn)行控制,其他寄存器含義可參考ARM文檔,這里不一一介紹。
3. 代碼實(shí)現(xiàn)
在Kernel中通過CONFIG_ARM64_AMU_EXTN宏進(jìn)行AMU代碼控制,Kernel最早支持AMU的功能,在這個(gè)patch中:
arm64: add support for the AMU extension v1
在arch/arm64/include/asm/sysreg.h中定義了AMU各寄存器的地址
/* Definitions for system register interface to AMU for ARMv8.4 onwards */ #define SYS_AM_EL0(crm, op2) sys_reg(3, 3, 13, (crm), (op2)) #define SYS_AMCR_EL0 SYS_AM_EL0(2, 0) #define SYS_AMCFGR_EL0 SYS_AM_EL0(2, 1) #define SYS_AMCGCR_EL0 SYS_AM_EL0(2, 2) #define SYS_AMUSERENR_EL0 SYS_AM_EL0(2, 3) #define SYS_AMCNTENCLR0_EL0 SYS_AM_EL0(2, 4) #define SYS_AMCNTENSET0_EL0 SYS_AM_EL0(2, 5) #define SYS_AMCNTENCLR1_EL0 SYS_AM_EL0(3, 0) #define SYS_AMCNTENSET1_EL0 SYS_AM_EL0(3, 1) #define SYS_AMEVCNTR0_EL0(n) SYS_AM_EL0(4 + ((n) >> 3), (n) & 7) #define SYS_AMEVTYPER0_EL0(n) SYS_AM_EL0(6 + ((n) >> 3), (n) & 7) #define SYS_AMEVCNTR1_EL0(n) SYS_AM_EL0(12 + ((n) >> 3), (n) & 7) #define SYS_AMEVTYPER1_EL0(n) SYS_AM_EL0(14 + ((n) >> 3), (n) & 7) /* AMU v1: Fixed (architecturally defined) activity monitors */ #define SYS_AMEVCNTR0_CORE_EL0 SYS_AMEVCNTR0_EL0(0) #define SYS_AMEVCNTR0_CONST_EL0 SYS_AMEVCNTR0_EL0(1) #define SYS_AMEVCNTR0_INST_RET_EL0 SYS_AMEVCNTR0_EL0(2) #define SYS_AMEVCNTR0_MEM_STALL SYS_AMEVCNTR0_EL0(3)
在arm-trusted-firmware中,也有AMU寄存器讀寫相關(guān)代碼,還包括上下電時(shí)AMU備份恢復(fù)的操作,代碼主要在lib/extensions/amu/aarch64/和lib/cpus/aarch64/cpuamu_helpers.S。
4. AMU的應(yīng)用
在內(nèi)核中有這么一個(gè)patch,目的是為了讓調(diào)度LoadTracking的FIE計(jì)算更準(zhǔn)確,
arm64: use activity monitors for frequency invariance
在此之前,Kernel中的freq scale計(jì)算都是通過cpufreq模塊記錄的當(dāng)前頻率和最大頻率來計(jì)算的,但是實(shí)際頻率可能在Kernel之外的地方發(fā)生變化,與Kernel cpufreq模塊記錄的當(dāng)前頻率不同,或者支持ACPI的cpufreq driver是不知道當(dāng)前頻率的。因此AMU就為獲取CPU平均頻率提供了一個(gè)方法。通過CPU_CYCLES和CNT_CYCLES的Counter計(jì)數(shù)來計(jì)算CPU頻率,具體代碼如下。
arch/arm64/kernel/topology.c
/* Initialize counter reference per-cpu variables for the current
CPU */
void init_cpu_freq_invariance_counters(void)
{
this_cpu_write(arch_core_cycles_prev,
read_sysreg_s(SYS_AMEVCNTR0_CORE_EL0));
this_cpu_write(arch_const_cycles_prev,
read_sysreg_s(SYS_AMEVCNTR0_CONST_EL0));
}
void topology_scale_freq_tick(void)
{
u64 prev_core_cnt, prev_const_cnt;
u64 core_cnt, const_cnt, scale;
int cpu =
smp_processor_id();
if
(!amu_freq_invariant())
return;
if
(!cpumask_test_cpu(cpu, amu_fie_cpus))
return;
const_cnt =
read_sysreg_s(SYS_AMEVCNTR0_CONST_EL0);
core_cnt = read_sysreg_s(SYS_AMEVCNTR0_CORE_EL0);
prev_const_cnt =
this_cpu_read(arch_const_cycles_prev);
prev_core_cnt =
this_cpu_read(arch_core_cycles_prev);
if (unlikely(core_cnt
<= prev_core_cnt ||
const_cnt <= prev_const_cnt))
goto
store_and_exit;
/*
* /core arch_max_freq_scale
* scale = ------- * --------------------
* /const SCHED_CAPACITY_SCALE
*
* See validate_cpu_freq_invariance_counters()
for details on
* arch_max_freq_scale and the use of
SCHED_CAPACITY_SHIFT。
*/
scale = core_cnt -
prev_core_cnt;
scale *=
this_cpu_read(arch_max_freq_scale);
scale = div64_u64(scale
>> SCHED_CAPACITY_SHIFT,
const_cnt - prev_const_cnt);
scale = min_t(unsigned
long, scale, SCHED_CAPACITY_SCALE);
this_cpu_write(freq_scale, (unsigned long)scale);
store_and_exit:
this_cpu_write(arch_core_cycles_prev, core_cnt);
this_cpu_write(arch_const_cycles_prev, const_cnt);
}
5. AMU與PMU的區(qū)別
前文提到AMU與PMU的設(shè)計(jì)目的不同。除此之外,還有哪些差異點(diǎn)呢?
AMU與PMU具有類似的特性,設(shè)計(jì)的目的有所不同,AMU是為了系統(tǒng)功耗性能管理,PMU是為了用戶態(tài)程序或者調(diào)試目的。從硬件角度都是對事件進(jìn)行計(jì)數(shù),我們同樣也可以使用PMU的統(tǒng)計(jì)結(jié)果進(jìn)行功耗和性能管理,為什么還要再引入AMU呢?
可能的原因是,PMU有多個(gè)使用場景,如果在性能功耗管理方案上使用了PMU,那么在性能調(diào)試時(shí)也要獲取PMU信息就可能產(chǎn)生沖突,例如使用強(qiáng)大的perf工具抓取PMU信息,尤其在Counter數(shù)量有限的情況下,沖突概率更大。引入AMU是為了給固定的資源專門用來做系統(tǒng)控制方案,從而釋放PMU資源用作其他目的。
PMU和AMU的軟件使用方式上,也有很大的不同。AMU通過system register interface就可以訪問,而PMU需要通過內(nèi)核的perf接口,軟件開銷遠(yuǎn)超過AMU。
下圖列出了兩者功能上的一些差異,

審核編輯:劉清
-
寄存器
+關(guān)注
關(guān)注
31文章
5505瀏覽量
128407 -
cpu
+關(guān)注
關(guān)注
68文章
11193瀏覽量
221962 -
計(jì)數(shù)器
+關(guān)注
關(guān)注
32文章
2304瀏覽量
97352 -
Cortex-A53
+關(guān)注
關(guān)注
0文章
33瀏覽量
21779
原文標(biāo)題:Perf monitor:從PMU到AMU
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
如何對Android 7.1 RK3288 PMU進(jìn)行調(diào)試呢
Revere AMU系統(tǒng)架構(gòu)參考指南
PMU測試儀的設(shè)計(jì)與研究
PMU量測點(diǎn)優(yōu)化配置新方法

AMU與PMU有哪些差異點(diǎn)呢?
評論