2013-04-26 110 views
3

我正在嘗試使用ARM Cortex-a9上的事件計數器(在Xilinx zynq EPP上)來計數週期。爲此,我已經調整了ARM的一些ARM示例代碼。我正在用GNU ARM EABI編譯器編寫這個裸機。ARM Cortex-a9事件計數器返回0

我理解使用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。我知道,因爲我能沒有問題,讀入週期計數器(PMCCNTR)我的PMU配置是否正確。我配置計數器的方式或閱讀方式可能存在問題。這個內聯彙編對我來說是非常新穎的,所以如果有人能指引我朝着正確的方向發展,我將永遠感激。

+1

參見[皮質-A8 PMNC](http://stackoverflow.com/questions/15492120/arm-cortex-a8-pmnc-read-gives-0-after-enabling-also-any-idea-suggestions)和[Cortex-a8 profiling](http://stackoverflow.com/questions/15524138/profling-on-arm-cortex-a8),它們可能會有所幫助。假設是Linux [perf_event_v7.c](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/arch/arm/kernel/perf_event_v7.c)用於Cortex CPU afaik。這可能是簡單的事情;我總是遇到'MCR' /'MRC'參數的問題。 – 2013-04-26 22:50:53

+1

您還有一些* in-line彙編程序*的問題。在很多情況下,你修改'計數器',但不要註釋這個。此外,您正在使用硬編碼的'r0','r1',但未指定此項。您可以在同一個'asm'語句中將多個* asm操作碼*組合在一起。只需使用'\ n';您不需要多次指定參數,但是要正確地指定參數。另外[A8](http://stackoverflow.com/questions/3247373/how-to-measure-program-execution-time-in-arm-cortex-a8-processor)[A8-2](http:// stackoverflow的.com /問題/ 9795132 /測量執行的時間啓動臂-皮質-使用A8-硬件計數器) – 2013-04-26 23:03:59

回答

2

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(); 

}