2015-09-16 2 views
10

Я хотел бы подсчитать количество инструкций за цикл, выполненных на процессоре ARM cortex-M4 (или cortex-M3).Счетчики ARM M4 в цикле (IPC)

Что нужно, это: количество инструкций (выполняемых во время выполнения) кода я хочу профилировать и число циклов, что код требуется для выполнения.

1 - Количество циклов

Используйте счетчик цикла довольно легко и просто.

volatile unsigned int *DWT_CYCCNT ; 
volatile unsigned int *DWT_CONTROL ; 
volatile unsigned int *SCB_DEMCR ; 

void reset_timer(){ 
    DWT_CYCCNT = (int *)0xE0001004; //address of the register 
    DWT_CONTROL = (int *)0xE0001000; //address of the register 
    SCB_DEMCR = (int *)0xE000EDFC; //address of the register 
    *SCB_DEMCR = *SCB_DEMCR | 0x01000000; 
    *DWT_CYCCNT = 0; // reset the counter 
    *DWT_CONTROL = 0; 
} 

void start_timer(){ 
    *DWT_CONTROL = *DWT_CONTROL | 1 ; // enable the counter 
} 

void stop_timer(){ 
    *DWT_CONTROL = *DWT_CONTROL | 0 ; // disable the counter  
} 

unsigned int getCycles(){ 
    return *DWT_CYCCNT; 
} 

main(){ 
    .... 
    reset_timer(); //reset timer 
    start_timer(); //start timer 
    //Code to profile 
    ... 
    myFunction(); 
    ... 
    stop_timer(); //stop timer 
    numCycles = getCycles(); //read number of cycles 
    ... 
} 

2 - Количество инструкций

Я нашел некоторую документацию серфинга в Интернете, чтобы подсчитать количество команд, выполняемых ARM Cortex-M3 и Cortex-M4 (link):

# instructions = CYCCNT - CPICNT - EXCCNT - SLEEPCNT - LSUCNT + FOLDCNT 

Регистры, о которых они упоминают, задокументированы here (со страницы 11-13), и это адреса памяти для доступа к ним:

DWT_CYCCNT = 0xE0001004 
DWT_CONTROL = 0xE0001000 
SCB_DEMCR = 0xE000EDFC 
DWT_CPICNT = 0xE0001008 
DWT_EXCCNT = 0xE000100C 
DWT_SLEEPCNT = 0xE0001010 
DWT_LSUCNT = 0xE0001014 
DWT_FOLDCNT = 0xE0001018 

Регистр DWT_CONTROL используется для включения счетчиков, особенно счетчика циклов, как задокументировано here.

Но когда я попытался собрать все вместе, чтобы подсчитать количество инструкций, выполненных за цикл, мне не удалось.

Here есть небольшой справочник о том, как использовать их из gdb.

Нелегко, что некоторые регистры являются 8-битными регистрами (DWT_CPICNT, DWT_EXCCNT, DWT_SLEEPCNT, DWT_LSUCNT, DWT_FOLDCNT), и когда они переполняются, они вызывают событие. Я не нашел способ собрать это событие. Нет фрагмента кода, который объясняет, как это сделать или подпрограммы, подходящие для этого.

Похоже, что использование точек наблюдения из gdb по адресам этих регистров не работает. gdb не может остановиться, когда регистры меняют значение. Например. на DWT_LSUCNT:

(gdb) watch *0xE0001014 

Обновление: Я нашел этот project на GitHub объясняет, как использовать DWT, ITM и ETM единиц. Но я не проверял, работает ли это! Я опубликую обновления.

Любая идея о том, как их использовать?

Спасибо!

+1

Возможно, слишком очевидно, но вы всегда вызываете reset_timer() перед выполнением любой другой функции, правильно? Не могли бы вы разместить код вызова, как минимальный пример? – Lundin

+3

Я бы предложил объявить регистры как '#define DWT_CYCCNT (* (volatile uint32_t *) 0xE0001004ul) вместо этого. – Lundin

+0

Являются ли события не отладки событий, которые могут вызвать исключение отладочного монитора? – Notlikethat

ответ

1

Я понятия не имею, как использовать регистры так, как вы хотите их использовать. Но вот как я занимаюсь измерительными циклами.

Убедитесь, что вы включили счетчик на SysTick Control and Status Register. С соответствующими заголовками вы должны иметь доступ к регистрам SysTick как структуре.

Измерьте количество циклов, выполняемых функцией счетчика. Это позже вычитается из любых измерений.

SysTick->VAL = 0; // set 0 
    // Measure delay on measurement 
    __disable_irq(); 
    a = (uint32_t) SysTick->VAL; 
    //... measuring zero instructions 
    b = (uint32_t) SysTick->VAL; 
    __enable_irq(); 
    measure_delay = a - b; 

Теперь измерить функцию.

SysTick->VAL = 0; 
__disable_irq(); 
a = (uint32_t) SysTick->VAL; 

//Assuming this function doesn't require interruptions 

// INSERT CODE TO BE PROFILED 
function_to_be_examined(); 

b = (uint32_t) SysTick->VAL; 
__enable_irq(); 
cycles_profiled_code = a - b - measure_delay; 

Надеюсь, это поможет.

+0

Обратите внимание, что это имеет очень грубую гранулярность, поскольку SysTick «обычно» установлен на переполнение каждый, например, 1мс. Код в вопросе измеряет точное количество циклов. – Darhuuk

+0

Хорошо отметил. Верный. Примерно, если скорость процессора составляет 100 МГц, 1 мс позволит измерить около 100 000 циклов. Этот метод является точным для функций, которые занимают до десятков тысяч циклов. – Toani

2

В примере кода, который вы указали, возникла проблема при очистке бит разрешения. Вы должны очистить бит петь «И» не «OR»:

*DWT_CONTROL = *DWT_CONTROL & 0xFFFFFFFE ; // disable the counter by clearing the enable bit 
1

Я думаю, если вы хотите измерить циклы точности, используя отладчик является хорошим выбором. Keil-MDK может накапливать государственный регистр и не будет переполняться. результат в отладчике совпадает с результатом с использованием DWT.

Если вы хотите измерить другие значения, т.е. FOLDCNT, используя трассировку в Keil-MDK -> Debug -> Setting -> Trace -> Trace Enable.

При этом при отладке в окне трассировки Windows выбирает событие трассировки, значение этого 8-битного регистра может быть собрано и добавлено вместе с Keil.

Это кажется немного глупым, но я не знаю, как собрать событие переполнения, я думаю, что это событие может быть отправлено только ITM, потому что либо DWT, либо ITM - это отдельный компонент из программы. если мы хотим собрать мероприятие в программе клиента, действие collect должно будет влиять на точность результата.

ITM? ЭТМ? CoreSight? DWT? AHB?