Счетчики инструкций ARC M4 за цикл (IPC)
Я хотел бы посчитать количество инструкций за цикл, выполненных на процессоре ARM cortex-M4 (или cortex-M3).
Это необходимо: количество инструкций (выполненных во время выполнения) кода, который я хочу профилировать, и количество циклов, которые код выполняет для выполнения.
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 ( ссылка):
# instructions = CYCCNT - CPICNT - EXCCNT - SLEEPCNT - LSUCNT + FOLDCNT
Регистры, которые они упоминают, описаны здесь (со страницы 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 используется для включения счетчиков, особенно счетчика циклов, как описано здесь.
Но когда я попытался собрать все вместе, чтобы посчитать количество инструкций, выполненных за цикл, у меня ничего не получилось.
Здесь есть небольшое руководство о том, как использовать их из GDB.
Что непросто, так это то, что некоторые регистры являются 8-битными регистрами (DWT_CPICNT, DWT_EXCCNT, DWT_SLEEPCNT, DWT_LSUCNT, DWT_FOLDCNT) и при переполнении запускают событие. Я не нашел способ собрать это событие. Там нет фрагмента кода, который объясняет, как это сделать или прерывать подпрограммы, подходящие для этого.
Более того, кажется, что использование точек наблюдения из GDB на адресах этих регистров не работает. GDB не может остановиться, когда регистры меняют значение. Например, DWT_LSUCNT:
(gdb) watch *0xE0001014
Обновление: я нашел этот проект на GitHub, объясняющий, как использовать модули DWT, ITM и ETM. Но я не проверял, работает ли это! Я буду публиковать обновления.
Есть идеи о том, как их использовать?
Спасибо!
3 ответа
Приведенный вами пример кода имеет проблему при очистке бита включения. Вы должны очистить бит петь "И", а не "ИЛИ":
*DWT_CONTROL = *DWT_CONTROL & 0xFFFFFFFE ; // disable the counter by clearing the enable bit
Я думаю, если вы хотите измерить циклы точности, использование отладчика является хорошим выбором. Keil-MDK может накапливать государственный реестр и не будет переполнен. результат в отладчике такой же, как результат с использованием DWT.
если вы хотите измерить другие значения, например FOLDCNT, используйте трассировку в Keil-MDK -> Debug -> Setting -> Trace -> Trace Enable.
При этом во время отладки в окне трассировки выберите событие трассировки, значение этого 8-битного регистра может быть собрано и добавлено Keil.
Это кажется немного глупым, но я не знаю, как собрать событие переполнения, я думаю, что это событие может быть отправлено только в ITM, потому что либо DWT, либо ITM являются отдельным компонентом вне программы. если мы хотим собрать событие в программе клиента, действие по сбору должно повлиять на точность результата.
ITM? ЭТМ? CoreSight? DWT?AHB?
Я понятия не имею, как использовать регистры так, как вы хотите их использовать. Но вот как я имею дело с измерительными циклами.
Убедитесь, что вы включили счетчик в регистре управления и состояния SysTick. С соответствующими заголовками у вас должен быть доступ к регистрам 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;
Я надеюсь, что это помогает.