我目前正在尝试使用ARM Cortex-a9(在Xilinx zynq EPP上)上的事件计数器来计算周期。为此,我已经从ARM调整了一些ARM示例代码。我正在用GNU ARM EABI编译器编写这个裸机。
我理解PMU的使用方式是首先必须启用PMU。
void enable_pmu (void){
asm volatile( "MRC p15, 0, r0, c9, c12, 0\n\t"
"ORR r0, r0, #0x01\n\t"
"MCR p15, 0, r0, c9, c12, 0\n\t"
);
}
然后你配置性能计数器来计算某种类型的事件(在这种情况下,0x11
为周期)
void config_pmn(unsigned counter,int event){
asm volatile( "AND %[counter], %[counter], #0x1F\n\t" :: [counter] "r" (counter)); //Mask to leave only bits 4:0
asm volatile( "MCR p15, 0, %[counter], c9, c12, 5\n\t" :: [counter] "r" (counter)); //Write PMSELR Register
asm volatile( "ISB\n\t"); //Synchronize context
asm volatile( "MCR p15, 0, %[event], c9, c13, 1\n\t" :: [event] "r" (counter)); //Write PMXEVTYPER Register
}
然后启用事件计数器
void enable_pmn(int counter){
asm volatile( "MOV r1, #0x1\n\t");
asm volatile( "MOV r1, r1, LSL %[counter]\n\t" :: [counter] "r" (counter));
asm volatile( "MCR p15, 0, r1, c9, c12, 1\n\t"); //Write PMCNTENSET Register
}
之后立即重置事件计数器
void reset_pmn(void){
asm volatile( "MRC p15, 0, r0, c9, c12, 0\n\t"); //Read PMCR
asm volatile( "ORR r0, r0, #0x2\n\t"); //Set P bit (Event counter reset)
asm volatile( "MCR p15, 0, r0, c9, c12, 0\n\t"); //Write PMCR
}
让您的应用程序运行并读取事件计数器
int read_pmn(int counter){
int value;
asm volatile( "AND %0,%0, #0x1F\n\t" :: "r" (counter)); //Mask to leave only bits 4:0
asm volatile( "MCR p15, 0, %[counter], c9, c12, 5\n\t" ::[counter] "r" (counter)); //Write PMSELR Register
asm volatile( "ISB\n\t"); //Synchronize context
asm volatile( "MRC p15, 0,%[value] , c9, c13, 2\n\t" : [value] "=r" (value)); //Read current PMNx Register
return value;
}
然后禁用事件计数器
void disable_pmn(int counter){
asm volatile( "MOV r1, #0x1\n\t");
asm volatile( "MOV r1, r1, LSL %[counter] \n\t":: [counter] "r" (counter));
asm volatile( "MCR p15, 0, r1, c9, c12, 2\n\t"); //Write PMCNTENCLR Register
}
和pmu。
void disable_pmu (void){
asm volatile( "MRC p15, 0, r0, c9, c12, 0\n\t"
"BIC r0, r0, #0x01\n\t"
"MCR p15, 0, r0, c9, c12, 0\n\t"
);
}
然而,当我尝试读取存储在事件计数器中的值时,我得到0.我知道我的PMU配置正确,因为我能够毫无问题地读取循环计数器(PMCCNTR
)。可能我配置计数器的方式或我读取它的方式有问题。这个内联装配对我来说是一个新手,所以如果有人能指出我正确的方向,我将永远感激。
答案 0 :(得分:3)
ARM Architecture Reference概述"所需事件"的C.12.8.5部分,我发现Zynq仅支持最少的PMU事件。正如您所描述的那样,尝试使用不受支持的事件只会给出零计数。
下面是一个小例子,说明如何操作协处理器15的寄存器来设置计数器并读取它们的值:
// My system has 6 configurable counters and a separate Cycle Count register.
// This will contain a nice human-readable name for the configured counters.
const char* cpu_name[7] = { "", "", "", "", "", "", "CCNT" };
typedef struct {
u32 reg[7]; // 6 configurables and the cycle count
} cpu_perf;
inline u32 _read_cpu_counter(int r) {
// Read PMXEVCNTR #r
// This is done by first writing the counter number to PMSELR and then reading PMXEVCNTR
u32 ret;
asm volatile ("MCR p15, 0, %0, c9, c12, 5\t\n" :: "r"(r)); // Select event counter in PMSELR
asm volatile ("MRC p15, 0, %0, c9, c13, 2\t\n" : "=r"(ret)); // Read from PMXEVCNTR
return ret;
}
inline void _setup_cpu_counter(u32 r, u32 event, const char* name) {
cpu_name[r] = name;
// Write PMXEVTYPER #r
// This is done by first writing the counter number to PMSELR and then writing PMXEVTYPER
asm volatile ("MCR p15, 0, %0, c9, c12, 5\t\n" :: "r"(r)); // Select event counter in PMSELR
asm volatile ("MCR p15, 0, %0, c9, c13, 1\t\n" :: "r"(event)); // Set the event number in PMXEVTYPER
}
void init_cpu_perf() {
// Disable all counters for configuration (PCMCNTENCLR)
asm volatile ("MCR p15, 0, %0, c9, c12, 2\t\n" :: "r"(0x8000003f));
// disable counter overflow interrupts
asm volatile ("MCR p15, 0, %0, c9, c14, 2\n\t" :: "r"(0x8000003f));
// Select which events to count in the 6 configurable counters
// Note that both of these examples come from the list of required events.
_setup_cpu_counter(0, 0x04, "L1DACC");
_setup_cpu_counter(1, 0x03, "L1DFILL");
}
inline void reset_cpu_perf() {
// Disable all counters (PMCNTENCLR):
asm volatile ("MCR p15, 0, %0, c9, c12, 2\t\n" :: "r"(0x8000003f));
u32 pmcr = 0x1 // enable counters
| 0x2 // reset all other counters
| 0x4 // reset cycle counter
| 0x8 // enable "by 64" divider for CCNT.
| 0x10; // Export events to external monitoring
// program the performance-counter control-register (PMCR):
asm volatile ("MCR p15, 0, %0, c9, c12, 0\t\n" :: "r"(pmcr));
// clear overflows (PMOVSR):
asm volatile ("MCR p15, 0, %0, c9, c12, 3\t\n" :: "r"(0x8000003f));
// Enable all counters (PMCNTENSET):
asm volatile ("MCR p15, 0, %0, c9, c12, 1\t\n" :: "r"(0x8000003f));
}
inline cpu_perf get_cpu_perf() {
cpu_perf ret;
int r;
// Read the configurable counters
for (r=0; r<6; ++r) {
ret.reg[r] = _read_cpu_counter(r);
}
// Read CPU cycle count from the CCNT Register
asm volatile ("MRC p15, 0, %0, c9, c13, 0\t\n": "=r"(ret.reg[6]));
return ret;
}
int main() {
init_cpu_perf();
// Here's what a test looks like:
reset_cpu_perf();
/*
* ... Perform your operations
*/
cpu_perf results_1 = get_cpu_perf();
}