ARM Cortex-a9 event counters return 0

I'm currently trying to use event counters on ARM Cortex-a9 (on Xilinx zynq EPP) to count the loops. For this purpose I adapted some ARM example code from ARM. I program this bare metal using the GNU ARM EABI compiler.

As I understand it, the use of PMU is that you must first enable the 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" ); } 

then you set up a performance counter to count a specific type of event ( 0x11 for loops in this case)

 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 } 

Then you turn on the event counter

 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 } 

after that you immediately reset the event counter

 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 } 

you start your application and read the event counter

 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; } 

and then you turn off the event counter

 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 } 

and 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" ); } 

However, when I try to read the value stored in the event counter, I get 0. I know that my PMU is configured correctly, because I can read the loop counter ( PMCCNTR ) without any problems. There is probably a problem with how I set up the counter or how I read it. This built-in compilation is quite new for me, so if someone can point me in the right direction, I will be forever grateful.

+4
source share
1 answer

Section C.12.8.5 The ARM Architecture Reference describes Mandatory Events, and I found that Zynq only supports the bare minimum of PMU events. Trying to use unsupported events simply gives null values, as you described.

The following is a small example of how to manipulate the registers of the coprocessor 15 to configure the counters and read their values:

 // 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 what a test looks like: reset_cpu_perf(); /* * ... Perform your operations */ cpu_perf results_1 = get_cpu_perf(); } 
+3
source

All Articles