This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

OMAP3530 ES3.0 (Beagleboard RevC3 CPU) problem with using a programable counter to count cycles

Other Parts Discussed in Thread: OMAP3530

Hi.

 

I am using a BeagleBoard RevC3 board with OMAP3530. My board's documentation states I am using OMAP ES3.0 silicon, with r1p3 Cortex-A8 MPU and ROM revision 14.04.

I am trying to implement support to use the performance counters built into the ARM Cortex MPU to monitor linux processes. I am using a modified version of  linux  kernel (kernel-2.6.29) running on top of a microkernel.

The microkernel runs in ARM supervisor mode and linux runs is ARM user mode.

I have problems using my particular OMAP3530 ES3.0's Cortex-A8 MPU to count the function 0x11 (cycle counter) in conjunction with any of the programable counters even if I perform the correct procedure of:

  • disabling the PMNC (p15, 0, %0, c9, c12, 0) by reseting the E bit;
  • setting up the counter (in this case PMCNT0 although I've tried them all)
    • write 0 to PMNXSEL (p15, 0, %0, c9, c12, 5);
    • write 0x11 to EVTSEL (p15, 0, %0, c9, c13, 1);
    • bit set bit 0 of CNTENS (p15, 0, %0, c9, c12, 1);
  • enabling the PMNC by setting the E bit

This procedure worked from the microkernel for other functionalites (like instruction counting) from the begining. I wrote simple inine assembler tests. It did not work for cycles (counter remained at the preset value after count function was set to 0x11).

 

Once I tried to program any one of the programmable counters with function 0x11, they did not count any more. Since by apllying the same procedure I can count events for different functionalites like instructions, stalls, cache hits and misses it's not the procedure that's responsible.

 

Initially I was unable to use any of the programmable counters after linux booted up, even if applied the procedure above. After some digging I found out linux was idling a piece of hardware in OMAP3530 which can as a sideeffect disable the PMN counters.

The effect I saw was that when DEBUGAUTHSTATUS @ 0x54011fb8 got to be 0 (because some idling performed by linux) the programmable counters no longer counted when enabled by me (enabling both the PMNC and setting the proper bit in CNTENS).

I guess this register set is reflecting the part of hardware responsible for setting up hardware debug bit in the Cortex-A8 MPU, without which the programable counters are set not to count.

 

My fix for that is to temporarily disable a function in kernel init that causes stuff to get idled.

 

Now I am faced with the same problem I had at the microkernel level: OMAP3530's MPU is not counting for programble counters for function 0x11 (cycle count). I can count instructions, cache hits, etc with programable counters, but not cycles. The fixed functionality cycle counter (CCNT) works fine.

 

I am frustrated since I do not know if the programable counters not counting when using cycle counter as their count function is:

  • a software issue that can be solved by applying a patch or
  • hardware issue on
    •  my MPU alone,
    • OMAP3 ES3.0 silicon alone,
    • or described in the OMAP3530's errata...

 

Hope somebody has an idea,

Thank you,

Gabi Voiculescu

 

 

 

 

  • Hi Gabi,

    First I took a look at the Errata, both for the Cortex-A8 and the OMAP3530, neither have any issues related to your scenario.

    I looked further into the Cortex-A8 TRM. It appears tha Cortex-A8 does not support 0x11 for the EVTSEL register.

    I see 0x11 is available for the ARM11 family, but it appears to not be available for Cortex-A8. See excerpt from the Cortex-A8 TRM below:

    This excerpt is from  section "3.2.50 c9, Event Selection Register" and it is from "Table 3-98 Values for predefined events"

  • Hi Jeff.

    First of all thank you for your reply.

    Maybe I am not looking at the right cortex-a8 Manual.

    I have been using DDI0344J/DDI0344H Cortex-A8 TRM for my initial documentation and in there it is stated clearly on:

    • page 215, (3-113) for DDI0344J
    • page 211, (3-113) for DDI0344H

    Does DDI0344J/DDI0344H Cortex-A8 TRM not apply to my specific processor revision ES3.0?

    What version of the Cortex-A8 manual have you been using?

    As for the errata for OMAP3530, as far as I know it's not public, so maybe something needs to be added in there, if not for the cpu at least for the documentation.

     

  • Gabi,

    Thank you for clarifying.  I believe you are using version -r1p3 of the Cortex-A8.  You can find that information on this wiki article: http://wiki.omap.com/index.php/How_to_Find_the_Cortex-A8_Revision_of_your_OMAP35x

    If you are using version -r1p3, then the closest document you can find on the ARM web page is -r1p1. The specific document name is DDIO344. This would be accurate for you. I do not know why this interface has changed, but thanks for pointing this out this difference.  I have a benchmark running on this version of OMAP3530 with the -r1p3 Cortex-A8 using the CCNT function.  For some reason the access CCNT is different in this version of the Cortex-A8 than in the version of Documentation you show.

    Here is how the cycle counter is accessed in my benchmark.  I hope this helps you get moving again. My benchmark matches the documentation for -r1p1 To access CCNT.

    MCR P15, #0, R0, C9, C12, #0  // (R0=0xD)This resets CCNT counter to zero, set CCNT to measure clk/64, reset all counters including CCNT

    MCR P15, #0, R1, C9, C12, #1 // (R1=0x80000000) Enable cycle counter.  You can also use this register to enable any of the other 4 counters. bit s(3-0)

    MRC P15, #0, RD, C9, C13, #0 // This will read back CCNT since reset. CCNT is handled differently than the other counters, I think this is

    MRC P15, #0, RD, C9, C13, #1 // This is EVTSEL for setting up the other 4 clock functions. For counters other than CCNT, I think the inteface has not changed.

     

  • Hi.

     

    Thank you again for your answer.

     

    I was aware of the CCNT register providing the fixed function of counting cycles and was succesfull in using that feature. 

    What I wanted to do was to have a second counter to provide the same functionality, and that is why I was exploring using the programmable counters with function 0x11.

     

    This discussion has been very enlightening for me nevertheless.

    As your link says above, it looks like the OMAP35x's do not use a Cortex-A8 revision newer than r1p3.

    I now see this Cortex-A8 feature (counting cycles with programmable counters using event 0x11) only comes in the Cortex-A8 processors only since r2p0 (as seen by looking at the cortex-a8 documentation revC:  http://infocenter.arm.com/help/topic/com.arm.doc.ddi0344c/DDI0344C_cortex_a8_r2p0_trm.pdf. Probaly did not get synthetized corectly in r1pX.

  • Hi Gabi,

     

    I am very interested in your post as I am currently struggling with a problem similar to your initial one of not getting the event counters to work. I am also trying to do this on a linux device, namely a Nokia N900 smartphone running Maemo 5 with a Linux 2.8.28 kernel. I get the cycle counter working, but the four event counters always contain 0 regardless of the event I select. I would really appreciate it if you had any idea of what I'm doing wrong, if there is a hardware bug causing this, or if you perhaps have any suggestions on how I can get this working? Below is the procedure I follow.

    As you, I first avoid that the kernel puts hardware to sleep by altering some kernel parameters from the command line as follows:

     

    echo 0 > /sys/power/clocks_off_while_idle

    echo 0 > /sys/power/enable_off_mode

    echo 0 > /sys/power/sleep_while_idle

    echo 0 > /sys/power/voltage_off_while_idle

     

    While this prevents the cycle counter from re-setting upon an idle-system, it doesn't seem to get the event counters going. The code I use is below, along with the output.  This is run in the init-method of a kernel module, and hence in privileged mode:

     

    void dumpregs() {

      u32 val;

      unsigned int cnt;

     

      printk("PMU Register Contents:\n");

     

      asm volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));

      printk("  PMNC     = 0x%08x\n", val);

     

      asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r" (val));

      printk("  CNTENS   = 0x%08x\n", val);

     

      asm volatile("mrc p15, 0, %0, c9, c14, 1" : "=r" (val));

      printk("  INTENS   = 0x%08x\n", val);

     

      asm volatile("mrc p15, 0, %0, c9, c12, 3" : "=r" (val));

      printk("  FLAGS    = 0x%08x\n", val);

     

      asm volatile("mrc p15, 0, %0, c9, c12, 5" : "=r" (val));

      printk("  SELECT   = 0x%08x\n", val);

     

      asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));

      printk("  CCNT     = 0x%08x\n", val);

     

      for (cnt = 0; cnt < 4; cnt++) {

        asm volatile("mcr p15, 0, %0, c9, c12, 5" : : "r" (cnt));

        asm volatile("mrc p15, 0, %0, c9, c13, 2" : "=r" (val));

        printk("  CNT %d    = 0x%08x\n", cnt, val);

     

        asm volatile("mrc p15, 0, %0, c9, c13, 1" : "=r" (val));

        printk("  EVTSEL %d = 0x%08x\n", cnt, val);

      }

    }

     

    void testcounters() {

      uint32_t value;

     

      // Dump regs

      dumpregs();

     

      // Disable all individual counters

      asm volatile("mov r0, #0x8000000F");

      asm volatile("mcr p15, 0, r0, c9, c12, 2");

     

      // Disable the PMNC

      asm volatile("MRC     p15, 0, r0, c9, c12, 0"); // Read PMNC

      asm volatile("ORR     r0, r0, #0x0"); // Disable

      asm volatile("MCR     p15, 0, r0, c9, c12, 0"); // Write PMNC

     

      // Select register and event

      asm volatile("mov r0, #0x0");

      asm volatile("mcr p15, 0, r0, c9, c12, 5");

      asm volatile("mov r0, #0x1");

      asm volatile("mcr p15, 0, r0, c9, c13, 1");

     

      // Enable all individual counters

      asm volatile("mov r0, #0x8000000F");

      asm volatile("mcr p15, 0, r0, c9, c12, 1");

     

      // Enable PMNC

      asm volatile("MRC     p15, 0, r0, c9, c12, 0"); // Read PMNC

      asm volatile("ORR     r0, r0, #0x7"); // Enable and re-set

      asm volatile("MCR     p15, 0, r0, c9, c12, 0"); // Write PMNC

     

      // Dump regs

      dumpregs();

     

      // Run testfunction

      printk("Profiling printk!\n");

     

      // Dump regs

      dumpregs();

    }

     

    int init_module(void) {

      testcounters();

      return 0;

    }

     

    When I do a dmsg, this is the output:

     

    [ 7645.950500] PMU Register Contents:

    [ 7645.950531]   PMNC     = 0x41002001

    [ 7645.950561]   CNTENS   = 0x8000000f

    [ 7645.950561]   INTENS   = 0x00000000

    [ 7645.950561]   FLAGS    = 0x80000000

    [ 7645.950592]   SELECT   = 0x00000003

    [ 7645.950592]   CCNT     = 0xadf613a8

    [ 7645.950592]   CNT 0    = 0x00000000

    [ 7645.950622]   EVTSEL 0 = 0x00000001

    [ 7645.950622]   CNT 1    = 0x00000000

    [ 7645.950653]   EVTSEL 1 = 0x00000000

    [ 7645.950653]   CNT 2    = 0x00000000

    [ 7645.950653]   EVTSEL 2 = 0x00000000

    [ 7645.950683]   CNT 3    = 0x00000000

    [ 7645.950683]   EVTSEL 3 = 0x00000000

    [ 7645.950683] PMU Register Contents:

    [ 7645.950714]   PMNC     = 0x41002001

    [ 7645.950714]   CNTENS   = 0x8000000f

    [ 7645.950714]   INTENS   = 0x00000000

    [ 7645.950744]   FLAGS    = 0x80000000

    [ 7645.950744]   SELECT   = 0x00000000

    [ 7645.950775]   CCNT     = 0x00007f1e

    [ 7645.950775]   CNT 0    = 0x00000000

    [ 7645.950775]   EVTSEL 0 = 0x00000001

    [ 7645.950805]   CNT 1    = 0x00000000

    [ 7645.951171]   EVTSEL 1 = 0x00000000

    [ 7645.951171]   CNT 2    = 0x00000000

    [ 7645.951202]   EVTSEL 2 = 0x00000000

    [ 7645.951202]   CNT 3    = 0x00000000

    [ 7645.951416]   EVTSEL 3 = 0x00000000

    [ 7645.951416] Profiling printk!

    [ 7645.951446] PMU Register Contents:

    [ 7645.951446]   PMNC     = 0x41002001

    [ 7645.951446]   CNTENS   = 0x8000000f

    [ 7645.951477]   INTENS   = 0x00000000

    [ 7645.951477]   FLAGS    = 0x80000000

    [ 7645.951507]   SELECT   = 0x00000003

    [ 7645.951507]   CCNT     = 0x00062a7c

    [ 7645.951507]   CNT 0    = 0x00000000

    [ 7645.951538]   EVTSEL 0 = 0x00000001

    [ 7645.951538]   CNT 1    = 0x00000000

    [ 7645.951538]   EVTSEL 1 = 0x00000000

    [ 7645.951568]   CNT 2    = 0x00000000

    [ 7645.951568]   EVTSEL 2 = 0x00000000

    [ 7645.951599]   CNT 3    = 0x00000000

    [ 7645.951599]   EVTSEL 3 = 0x00000000

     

    As you can see, the cycle counter works, but not the event counters. I was hoping perhaps you have some idea of what is happening...?

     

    Thank you,

    Stein Kristiansen