10-05-2020 06:02 AM
Hi!
I'm trying to enable some event counters to monitor L2D_Cache (0x16), L2D_Cache_Refill (0x17), and L2D_Cache_WB(0x18). So far I was able to enable the event counters by writing 0x11 to the PMCR register (0x00003E04). I know I need to set the event type to monitor those events and I think I need to do that with PMCEID0 (0x00003E04) but I'm not sure.
My code is very basic right now, all I have is:
void enable_cache(){
Xil_Out32(PMCR,0x00000011);//Enables all counters and exports of events are enable
Xil_Out32(PMCEID0 + L2D_CACHE, 0xFFFFFFFF); //TODO: Make sure this is correct. I want to set it to monitor that
//enable counter
//set what they are counting
}
But I'm not super confident that I have it correct. I have PMCR, PMCEID0, and L2D_CACHE defined in a header file to the values I mentioned previously.
Would someone be able to tell me if I'm on the correct path? I've been using https://developer.arm.com/documentation/ddi0500/d/performance-monitor-unit/memory-mapped-register-summary?lang=en
and
https://www.xilinx.com/html_docs/registers/ug1087/ug1087-zynq-ultrascale-registers.html
For the registers and addresses.
I'm writing this for a ZCU106 Ultrascale+ board.
Thanks in advance!
10-23-2020 05:23 AM
I have figured out my problems with this question.
For anyone who finds this forum later the general format, you need to follow are as follow:
1) In the LAR register: write 0xFEC30FB0 to it (it will unlock writing to the registers). One thing I noticed is once it was unlocked it stayed unlocked. To know if yours is unlocked read from 0xFEC30FB4 and if it's 0x1 it's unlocked.
2) In the CR_EL0 register read modify it. Or what you read it with 0x1. This will enable the counters. Write to CR_EL0 with that new value.
3) Read from EVTYPERN_EL0 (where N is the counter you're modifying).
4) Modify what you read by ANDing it with 0xFFFFFB00 (this will help you modify only the bottom 8 bits).
5) After you AND it OR that value with the EventID that you want to monitor (List can be found here: https://developer.arm.com/documentation/ddi0500/d/performance-monitor-unit/events?lang=en)
6) write that value back to EVTYPERN_EL0
7) repeat steps 3-6 for all counters you're using
9) OR that value with however many counters you're using and write it back to CNTENSET_ELO.
10) Read from CR_EL0 again
11) OR that value with 0x2 and write it back to CR_EL0. This will clear all the counters.
At this point, the counters will be enabled and monitoring the events. To read from the register do the following:
1) If you're done monitoring those events read from CNTENCLR_EL0
2) OR that value with the same value you wrote to CNTENSET_EL0 and write that value to CNTENCLR_EL0
3) read from EVCNTRN_EL0 where N is the counter that you want to read. This is your result
if you don't want to disable the counter just do step 3
There's a possibility that you don't have to do all these steps but this is what worked for me.
10-07-2020 04:57 AM
An update: I know this isn't correct at all now. All I was doing was setting the address and reading it. I found the type register but can't find the address for some reason. I know I need the type register so I can tell it what to monitor, but still confused on how to do everything.
10-19-2020 05:12 AM
Another update and new problem. I found that I was using the wrong addresses. I should've been using absolute values as well as I should've been using all from the same "group". I was mixing from SMMU500 and A53_PMU_0, I should've just been using A53_PMU_0. So I changed that. My new problem is that it doesn't seem to be writing to the registers.
void enable_cache(){
uint32_t EVENT =0;
uint32_t PMCR = Xil_In32(CR_ELO);
printf("CR_ELO read: 0x%x\n",PMCR);
printf("CR_ELO OR: 0x%x\n",(PMCR | 0x00000001));
PMCR |= 0x00000001;
Xil_Out32(CR_ELO, PMCR );//Enables all counters
sleep(5);
printf("CR_ELO Read after write: 0x%x\n",Xil_In32(CR_ELO));
EVENT = Xil_In32(EVTYPER0_EL0);
printf("EVTYPER0_EL0 read: 0x%x\n",EVENT);
EVENT &= 0xFFFFFB00;
printf("EVTYPER0_EL0 AND: 0x%x\n",EVENT);
EVENT |= L2D_CACHE;
printf("EVTYPER0_EL0 OR: 0x%x\n",EVENT);
EVENT |= 0x50000000;
printf("EVTYPER0_EL0 OR # 2: 0x%x\n",EVENT);
Xil_Out32(EVTYPER0_EL0, EVENT);
sleep(1); //sleep for 1 second
printf("EVTYPER0_EL0 read after write: 0x%x\n",Xil_In32(EVTYPER0_EL0));
printf("CR_ELO read: 0x%x\n",Xil_In32(CR_ELO));
printf("CR_ELO OR (for clear): 0x%x\n",(Xil_In32(CR_ELO) | 0x00000010));
Xil_Out32(CR_ELO,Xil_In32(CR_ELO) | 0x00000010);//Enables all counters
printf("CR_ELO Read after write (for clear): 0x%x\n",Xil_In32(CR_ELO));
// printf("Event Counter: 0x%x\n",Xil_In32(PMEVCNTR0));
}
The code above outputs:
CR_ELO read: 0x0
CR_ELO OR: 0x1
CR_ELO Read after write: 0x0
EVTYPER0_EL0 read: 0x0
EVTYPER0_EL0 AND: 0x0
EVTYPER0_EL0 OR: 0x16
EVTYPER0_EL0 OR # 2: 0x50000016
EVTYPER0_EL0 read after write: 0x0
CR_ELO read: 0x0
CR_ELO OR (for clear): 0x10
CR_ELO Read after write (for clear): 0x0
My problem comes from the "CR_EL0 Read after write: 0x0" that shouldn't be that and I can't figure out why.
As you can see in the code I put "sleep(5)" in case it needs some time to set everything.
That didn't work either.
I'm stuck on that problem does anyone know why?
Thanks!
10-23-2020 05:23 AM
I have figured out my problems with this question.
For anyone who finds this forum later the general format, you need to follow are as follow:
1) In the LAR register: write 0xFEC30FB0 to it (it will unlock writing to the registers). One thing I noticed is once it was unlocked it stayed unlocked. To know if yours is unlocked read from 0xFEC30FB4 and if it's 0x1 it's unlocked.
2) In the CR_EL0 register read modify it. Or what you read it with 0x1. This will enable the counters. Write to CR_EL0 with that new value.
3) Read from EVTYPERN_EL0 (where N is the counter you're modifying).
4) Modify what you read by ANDing it with 0xFFFFFB00 (this will help you modify only the bottom 8 bits).
5) After you AND it OR that value with the EventID that you want to monitor (List can be found here: https://developer.arm.com/documentation/ddi0500/d/performance-monitor-unit/events?lang=en)
6) write that value back to EVTYPERN_EL0
7) repeat steps 3-6 for all counters you're using
9) OR that value with however many counters you're using and write it back to CNTENSET_ELO.
10) Read from CR_EL0 again
11) OR that value with 0x2 and write it back to CR_EL0. This will clear all the counters.
At this point, the counters will be enabled and monitoring the events. To read from the register do the following:
1) If you're done monitoring those events read from CNTENCLR_EL0
2) OR that value with the same value you wrote to CNTENSET_EL0 and write that value to CNTENCLR_EL0
3) read from EVCNTRN_EL0 where N is the counter that you want to read. This is your result
if you don't want to disable the counter just do step 3
There's a possibility that you don't have to do all these steps but this is what worked for me.