【问题标题】:ARM Cortex-a9 event counters return 0ARM Cortex-a9 事件计数器返回 0
【发布时间】:2013-04-20 15:05:54
【问题描述】:

我目前正在尝试使用 ARM Cortex-a9(在 Xilinx zynq EPP 上)上的事件计数器来计算周期。为此,我从 ARM 改编了一些 ARM 示例代码。我正在使用 GNU ARM EABI 编译器对这个裸机进行编程。

我理解使用 PMU 的方式是您首先必须启用 PMU。

void enable_pmu (void){
    asm volatile(   "MRC     p15, 0, r0, c9, c12, 0\n\t"
                    "ORR     r0, r0, #0x01\n\t"
                    "MCR     p15, 0, r0, c9, c12, 0\n\t"
    );
}

然后您将性能计数器配置为对某种类型的事件进行计数(在这种情况下,0x11 用于循环)

void config_pmn(unsigned counter,int event){
    asm volatile(   "AND     %[counter], %[counter], #0x1F\n\t" :: [counter] "r" (counter));    //Mask to leave only bits 4:0
    asm volatile(   "MCR     p15, 0, %[counter], c9, c12, 5\n\t" :: [counter] "r" (counter));   //Write PMSELR Register
    asm volatile(   "ISB\n\t");                                                                 //Synchronize context
    asm volatile(   "MCR     p15, 0, %[event], c9, c13, 1\n\t" :: [event] "r" (counter));       //Write PMXEVTYPER Register
}

然后你启用事件计数器

void enable_pmn(int counter){
    asm volatile(   "MOV     r1, #0x1\n\t");
    asm volatile(   "MOV     r1, r1, LSL %[counter]\n\t" :: [counter] "r" (counter));
    asm volatile(   "MCR     p15, 0, r1, c9, c12, 1\n\t");      //Write PMCNTENSET Register
}

在此之后,您立即重置事件计数器

void reset_pmn(void){
    asm volatile(   "MRC     p15, 0, r0, c9, c12, 0\n\t");  //Read PMCR
    asm volatile(   "ORR     r0, r0, #0x2\n\t");            //Set P bit (Event counter reset)
    asm volatile(   "MCR     p15, 0, r0, c9, c12, 0\n\t");  //Write PMCR
}

您让您的应用程序运行并读取事件计数器

int read_pmn(int counter){
    int value;
    asm volatile(   "AND     %0,%0, #0x1F\n\t" :: "r" (counter));          //Mask to leave only bits 4:0
    asm volatile(   "MCR     p15, 0, %[counter], c9, c12, 5\n\t" ::[counter] "r" (counter));        //Write PMSELR Register
    asm volatile(   "ISB\n\t");                                                                     //Synchronize context
    asm volatile(   "MRC     p15, 0,%[value] , c9, c13, 2\n\t" : [value] "=r" (value));                 //Read current PMNx Register
    return value;
}

然后你禁用事件计数器

void disable_pmn(int counter){
     asm volatile(  "MOV     r1, #0x1\n\t");
     asm volatile(  "MOV     r1, r1, LSL %[counter] \n\t":: [counter] "r" (counter));
     asm volatile(  "MCR     p15, 0, r1, c9, c12, 2\n\t");  //Write PMCNTENCLR Register
}

和 pmu。

void disable_pmu (void){
    asm volatile(   "MRC     p15, 0, r0, c9, c12, 0\n\t"
                    "BIC     r0, r0, #0x01\n\t"
                    "MCR     p15, 0, r0, c9, c12, 0\n\t"
    );
}

但是,当我尝试读取存储在事件计数器中的值时,我得到 0。我知道我的 PMU 配置正确,因为我能够毫无问题地读取循环计数器 (PMCCNTR)。我配置计数器的方式或读取它的方式可能存在问题。这个内联汇编的东西对我来说很新,所以如果有人能指出我正确的方向,我将永远感激不尽。

【问题讨论】:

  • 请参阅Cortex-A8 pmncCortex-a8 profiling,它们可能会有所帮助。而 Linux perf_event_v7.c 应该是用于 Cortex CPU 的 afaik。这可能很简单;我总是对MCR/MRC 参数有问题。
  • 内联汇编器也有一些问题。在很多情况下,您会修改counter,但不要对此进行注释。此外,您使用的是硬编码的r0,r1,但没有指定。您可以将多个 asm op-codes 分组到同一个 asm 语句中。只需使用\n;您不需要多次指定参数,但要正确设置。还有A8A8-2

标签: arm inline-assembly performancecounter


【解决方案1】:

ARM Architecture Reference 的第 C.12.8.5 节概述了“必需事件”,我发现 Zynq 仅支持最少的 PMU 事件。正如您所描述的,尝试使用不受支持的事件只会给出零计数。

下面附上一个小例子,说明如何操纵协处理器 15 的寄存器来设置计数器并读取它们的值:

// My system has 6 configurable counters and a separate Cycle Count register.
// This will contain a nice human-readable name for the configured counters.
const char* cpu_name[7] = { "", "", "", "", "", "", "CCNT" };

typedef struct {
  u32 reg[7];       // 6 configurables and the cycle count
} cpu_perf;


inline u32 _read_cpu_counter(int r) {
  // Read PMXEVCNTR #r
  // This is done by first writing the counter number to PMSELR and then reading PMXEVCNTR
  u32 ret;
  asm volatile ("MCR p15, 0, %0, c9, c12, 5\t\n" :: "r"(r));      // Select event counter in PMSELR
  asm volatile ("MRC p15, 0, %0, c9, c13, 2\t\n" : "=r"(ret));    // Read from PMXEVCNTR
  return ret;
}

inline void _setup_cpu_counter(u32 r, u32 event, const char* name) {
  cpu_name[r] = name;

  // Write PMXEVTYPER #r
  // This is done by first writing the counter number to PMSELR and then writing PMXEVTYPER
  asm volatile ("MCR p15, 0, %0, c9, c12, 5\t\n" :: "r"(r));        // Select event counter in PMSELR
  asm volatile ("MCR p15, 0, %0, c9, c13, 1\t\n" :: "r"(event));    // Set the event number in PMXEVTYPER
}

void init_cpu_perf() {

  // Disable all counters for configuration (PCMCNTENCLR)
  asm volatile ("MCR p15, 0, %0, c9, c12, 2\t\n" :: "r"(0x8000003f));

  // disable counter overflow interrupts
  asm volatile ("MCR p15, 0, %0, c9, c14, 2\n\t" :: "r"(0x8000003f));


  // Select which events to count in the 6 configurable counters
  // Note that both of these examples come from the list of required events.
  _setup_cpu_counter(0, 0x04, "L1DACC");
  _setup_cpu_counter(1, 0x03, "L1DFILL");

}


inline void reset_cpu_perf() {

  // Disable all counters (PMCNTENCLR):
  asm volatile ("MCR p15, 0, %0, c9, c12, 2\t\n" :: "r"(0x8000003f));

  u32 pmcr  = 0x1    // enable counters
            | 0x2    // reset all other counters
            | 0x4    // reset cycle counter
            | 0x8    // enable "by 64" divider for CCNT.
            | 0x10;  // Export events to external monitoring

  // program the performance-counter control-register (PMCR):
  asm volatile ("MCR p15, 0, %0, c9, c12, 0\t\n" :: "r"(pmcr));

  // clear overflows (PMOVSR):
  asm volatile ("MCR p15, 0, %0, c9, c12, 3\t\n" :: "r"(0x8000003f));

  // Enable all counters (PMCNTENSET):
  asm volatile ("MCR p15, 0, %0, c9, c12, 1\t\n" :: "r"(0x8000003f));

}

inline cpu_perf get_cpu_perf() {
  cpu_perf ret;
  int r;

  // Read the configurable counters
  for (r=0; r<6; ++r) {
    ret.reg[r] = _read_cpu_counter(r);
  }

  // Read CPU cycle count from the CCNT Register
  asm volatile ("MRC p15, 0, %0, c9, c13, 0\t\n": "=r"(ret.reg[6]));

  return ret;
}

int main() {
  init_cpu_perf();

  // Here's what a test looks like:
  reset_cpu_perf();
  /*
   * ... Perform your operations
   */
  cpu_perf results_1 = get_cpu_perf();

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多