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

Parents
  • 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="

Reply
  • 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="

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