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.


This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Time measurement using Performance Monitors Register on Cortex-A72

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:

  • PMUSERENR_EL0 : 0xD
  • PMCR_EL0 : 0x41023001
  • PMCNTENSET_EL0 : 0x80000000

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 ,

    Please find below a simple code to use PMU counters.

    Best Regards,
    Willy Wolff
    Arm 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;
    }