2013-04-26 3 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 
} 

и ЦУП.

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) без проблем. Вероятно, есть проблема с тем, как я настраиваю счетчик или как его читаю. Этот встроенный сборник для меня совсем новый, поэтому, если кто-то может указать мне в правильном направлении, я буду навсегда благодарен.

+1

См [Cortex-A8 pmnc] (http://stackoverflow.com/questions/15492120/arm-cortex-a8-pmnc-read-дает-0-after-enable-also-any-idea-предложения) и [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 afaik. Это может быть что-то простое; У меня всегда возникают проблемы с параметрами 'MCR' /' MRC'. –

+1

У вас также есть некоторые проблемы с * встроенным ассемблером *. Во многих случаях вы изменяете 'counter', но не комментируете это. Кроме того, вы используете жестко закодированные 'r0',' r1', но не указали это. Вы можете сгруппировать несколько * opm-кодов * 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/мера времени выполнение-на-руки-Cortex-A8-помощи-аппаратно-счетчик) –

ответ

2

В разделе C.12.8.5 из ARM Architecture Reference показаны «Обязательные события», и я обнаружил, что 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(); 

}