Please note: We are aware of an issue affecting replies on the Arm Community forums, which may not be loading as expected.
We apologize for any inconvenience and appreciate your patience while we investigate and work to resolve the issue.
Thank you for your understanding.
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; }