ARM 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 - Количество инструкций

Я нашел документацию в Интернете, чтобы подсчитать количество инструкций, выполненных руками 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. Но не проверял, работает ли! Я буду размещать обновления.

Есть идеи, как их использовать?

Спасибо!


person FrankPak    schedule 16.09.2015    source источник
comment
Может быть, слишком очевидно, но вы всегда вызываете reset_timer () перед выполнением любой другой функции, правильно? Не могли бы вы опубликовать код вызова в качестве минимального примера?   -  person Lundin    schedule 16.09.2015
comment
Я бы предложил вместо этого объявить регистры как #define DWT_CYCCNT (*(volatile uint32_t*)0xE0001004ul).   -  person Lundin    schedule 16.09.2015
comment
Не являются ли события событиями отладки, которые вызывают исключение монитора отладки?   -  person Notlikethat    schedule 20.09.2015
comment
Я понятия не имею, какое событие генерируется. В документах arm ссылка они говорят о событиях, но я не нашел способа их собрать   -  person FrankPak    schedule 21.09.2015


Ответы (3)


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

*DWT_CONTROL = *DWT_CONTROL & 0xFFFFFFFE ; // disable the counter by clearing the enable bit
person hesham_EE    schedule 18.12.2015

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

если вы хотите измерить другие значения, например FOLDCNT, используя трассировку в Keil-MDK -> Отладка -> Настройка -> Трассировка -> Включить трассировку.

При этом во время отладки в окне трассировки выберите событие трассировки, значение этого 8-битного регистра может быть собрано и суммировано Кейлом.

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

ITM? ETM? CoreSight? DWT? AHB?

person ling jun kong    schedule 26.08.2017
comment
Вы узнали, как собирать события переполнения счетчика от выполнения программы, не влияя на них? - person Sil; 02.02.2020

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

Убедитесь, что вы включили счетчик на странице 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;

Я надеюсь, что это помогает.

person Toani    schedule 22.11.2015
comment
Обратите внимание, что это измерение с очень грубой степенью детализации, так как SysTick обычно настроен на переполнение, например, каждый раз. 1 мс. Код в вопросе измеряет точное количество циклов. - person AVH; 17.05.2017
comment
Хорошо замечено. Верный. Грубо говоря, если частота процессора составляет 100 МГц, 1 мс позволит измерить около 100 000 циклов. Этот метод точен для функций, которые занимают до десятков тысяч циклов. - person Toani; 16.11.2017