Hi all,
I am writing the Bare Metal program on ARM Cortex-A72. I follow some examples to configure the Performance Monitors Registers to do timing measurement.
The initializing step is as below:
Set bit EN, ER, CR (bit 0, 2, 3) of PMUSERENR_EL0 to 1
Set bit E, C, P (bit 0, 1, 2) of PMCR_EL0 to 1
Set bit C (bit 31) of PMCNTENSET_EL0 to 1
After initalize, I read back the register value is:
However, when I read the counter by reading the register PMCCNTR_EL0, I always get 0.
May I know any wrong with the setting? Or can you suggest any way to resolve it? Thanks a lot for your help
Dear Chanh,
Please find below a simple code to use PMU counters.
Best Regards,Willy WolffArm Partner Enablement Group
// some usefull registers to configure // https://developer.arm.com/documentation/ddi0595/2021-06/External-Registers/PMCR-EL0--Performance-Monitors-Control-Register // https://developer.arm.com/documentation/ddi0595/2021-06/AArch64-Registers/PMXEVTYPER-EL0--Performance-Monitors-Selected-Event-Type-Register // https://developer.arm.com/documentation/ddi0595/2021-06/AArch64-Registers/PMEVTYPER-n--EL0--Performance-Monitors-Event-Type-Registers .section .text, "ax", %progbits .global pe_pmu_start .global pe_pmu_stop .global pe_pmu_print .global pe_pmu_nr_cnt .global pe_pmu_ipc .type pe_pmu_start, %function pe_pmu_start: // SPME: Secure performance monitors enable. // This enables event counting exceptions from Secure state. mrs x9, MDCR_EL3 orr x9, x9, #(1 << 17) msr MDCR_EL3, x9 // cycle counter mov x9, xzr orr x9, x9, #(1 << 31) msr PMCNTENSET_EL0, x9 // first pmu counters mov x9, #0 msr PMSELR_EL0, x9 isb mov x10, #0x08 // UPDATE THIS WITH CUSTOM PMU EVENT NUMBER msr PMXEVTYPER_EL0, x10 isb mov x9, xzr orr x9, x9, #(1 << 0) msr PMCNTENSET_EL0, x9 isb // second pmu counters mov x9, #1 msr PMSELR_EL0, x9 isb mov x10, #0x14 // UPDATE THIS WITH CUSTOM PMU EVENT NUMBER msr PMXEVTYPER_EL0, x10 isb mov x9, xzr orr x9, x9, #(1 << 1) msr PMCNTENSET_EL0, x9 isb // third pmu counters mov x9, #2 msr PMSELR_EL0, x9 isb mov x10, #0x01 // UPDATE THIS WITH CUSTOM PMU EVENT NUMBER msr PMXEVTYPER_EL0, x10 isb mov x9, xzr orr x9, x9, #(1 << 2) msr PMCNTENSET_EL0, x9 isb // enable, reset mrs x9, PMCR_EL0 orr w9, w9, #(1 << 0) orr w9, w9, #(1 << 1) orr w9, w9, #(1 << 2) msr PMCR_EL0, x9 isb ret .type pe_pmu_stop, %function pe_pmu_stop: // disable mrs x9, PMCR_EL0 bfi w9, wzr, #0, #1 msr PMCR_EL0, x9 isb // cycles mrs x9, pmccntr_el0 adr x10, pmccnt_val str x9, [x10] // first counter mov x9, #0 msr PMSELR_EL0, x9 isb mrs x10, PMXEVCNTR_EL0 adr x11, pmu0_val str x10, [x11] // second counter mov x9, #1 msr PMSELR_EL0, x9 isb mrs x10, PMXEVCNTR_EL0 adr x11, pmu1_val str x10, [x11] // third counter mov x9, #2 msr PMSELR_EL0, x9 isb mrs x10, PMXEVCNTR_EL0 adr x11, pmu2_val str x10, [x11] ret .type pe_pmu_print, %function pe_pmu_print: str x30, [sp, #-16]! adr x0, pmccnt_string bl print adr x9, pmccnt_val ldr x0, [x9] bl print_hex bl newline adr x0, pmu0_string bl print adr x9, pmu0_val ldr x0, [x9] bl print_hex bl newline adr x0, pmu1_string bl print adr x9, pmu1_val ldr x0, [x9] bl print_hex bl newline adr x0, pmu2_string bl print adr x9, pmu2_val ldr x0, [x9] bl print_hex bl newline ldr x30, [sp], #16 ret .type pe_pmu_nr_cnt, %function pe_pmu_nr_cnt: str x30, [sp, #-16]! str x19, [sp, #-16]! // extract N in [15:11] mrs x9, PMCR_EL0 mov x10, #11 lsr x9, x9, x10 and x9, x9, #0x1f // save it accros function call mov x19, x9 adr x0, pmc_nr_string bl print mov x0, x19 bl print_hex bl newline ldr x19, [sp], #16 ldr x30, [sp], #16 ret .type pe_pmu_ipc, %function pe_pmu_ipc: adr x9, pmccnt_val ldr x2, [x9] adr x9, pmu0_val ldr x1, [x9] mov x3, #1000 mul x1, x1, x3 udiv x0, x1, x2 ret .balign 8 pmccnt_val: .dword 0 .balign 8 pmu0_val: .dword 0 .balign 8 pmu1_val: .dword 0 .balign 8 pmu2_val: .dword 0 .balign 4 pmc_nr_string: .asciz "pmc_nr=" .balign 4 pmccnt_string: .asciz "pmccnt=" .balign 4 pmu0_string: .asciz "pmu0=" .balign 4 pmu1_string: .asciz "pmu1=" .balign 4 pmu2_string: .asciz "pmu2=" .balign 4 ipc_string: .asciz "ipc="
Hi Willy Wolff,
Thanks a lot for your answer.
I also got the answer from another source and just share it here in case any other needs:
static void enable_cycle_counter_el0(void) { uint64_t val; /* Disable cycle counter overflow interrupt */ asm volatile("msr pmintenset_el1, %0" : : "r" ((uint64_t)(0 << 31))); /* Enable cycle counter */ asm volatile("msr pmcntenset_el0, %0" :: "r" (1 << 31)); /* Enable user-mode access to cycle counters. */ asm volatile("msr pmuserenr_el0, %0" :: "r" ((1 << 0) | (1 << 2))); /* Clear cycle counter and start */ asm volatile("mrs %0, pmcr_el0" : "=r" (val)); val |= ((1 << 0) | (1 << 2)); asm volatile("isb"); asm volatile("msr pmcr_el0, %0" :: "r" (val)); val = (1 << 27); asm volatile("msr pmccfiltr_el0, %0" :: "r" (val)); } static inline uint64_t read_pmccntr(void) { uint64_t val; asm volatile("mrs %0, pmccntr_el0" : "=r"(val)); return val; }