Счетчики ARM M4 для каждого цикла (IPC)

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

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

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. Но я не проверял, работает ли это! Я буду публиковать обновления.

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

Спасибо!

Ответ 1

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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

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

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