Счетчик циклов на ARM Cortex M4 (или M3)?
Я пытаюсь профилировать функцию C (которая вызывается из прерывания, но я могу извлечь ее и профилировать в другом месте) на Cortex M4.
Каковы возможности подсчета количества циклов, обычно используемых в этой функции? Функция должна работать с вершиной ~4000 циклов, так что RTC не вариант, я думаю, и ручной подсчет циклов от разборки может быть болезненным - и полезным только в случае усреднения, потому что я хотел бы профилировать в типичном потоке с типичной флэш-памятью шаблон использования.
Я слышал о регистрах счетчиков циклов и инструкциях MRC, но они, кажется, доступны для A8/11. Я не видел таких инструкций в Cortex-Mx Micros.
5 ответов
Посмотрите на регистр DWT_CYCCNT, определенный здесь. Обратите внимание, что этот регистр зависит от реализации. Кто производитель чипов? Я знаю, что реализация STM32 предлагает этот набор регистров.
Этот пост содержит инструкции по использованию регистра счетчика циклов DWT для определения времени. (См. Форму сообщения 11 декабря 2009 г. - 18:29)
Этот пост переполнения стека также является примером того, как DWT_CYCCNT.
Если ваша часть включает в себя CoreSight Embedded Trace Macrocell и у вас есть соответствующее аппаратное и программное обеспечение отладчика с поддержкой трассировки, вы можете напрямую профилировать код. Отладочное оборудование с поддержкой трассировки, конечно, дороже, и ваша плата должна быть спроектирована так, чтобы выводы заголовка порта трассировки были доступны в заголовке отладки. Поскольку эти контакты часто мультиплексируются для других функций, это не всегда возможно или практически невозможно.
В противном случае, если ваша цепочка инструментов включает в себя симулятор с точностью до цикла (например, тот, что доступен в Keil uVision), вы можете использовать его для анализа синхронизации кода. Имитатор предоставляет функции отладки, трассировки и профилирования, которые, как правило, более мощные и гибкие, чем те, которые доступны на чипе, поэтому даже если у вас есть оборудование для трассировки, имитатор все же может оказаться более простым решением.
Это просто проще:
[код]
#define start_timer() *((volatile uint32_t*)0xE0001000) = 0x40000001 // Enable CYCCNT register
#define stop_timer() *((volatile uint32_t*)0xE0001000) = 0x40000000 // Disable CYCCNT register
#define get_timer() *((volatile uint32_t*)0xE0001004) // Get value from CYCCNT register
/***********
* How to use:
* uint32_t it1, it2; // start and stop flag
start_timer(); // start the timer.
it1 = get_timer(); // store current cycle-count in a local
// do something
it2 = get_timer() - it1; // Derive the cycle-count difference
stop_timer(); // If timer is not needed any more, stop
print_int(it2); // Display the difference
****/
[/код]
Работает на Cortex M4: STM32F407VGT на плате CJMCU и просто считает необходимые циклы.
Расширение предыдущих ответов с помощью примера DWT_CYCCNT (STM32) в main
(похоже на мой другой пост).
Примечание: я также добавил метод задержки. Вы можете проверить stopwatch_delay
позвонив STOPWATCH_START
, бежать stopwatch_delay(ticks)
затем позвоните STOPWATCH_STOP
и проверить с CalcNanosecondsFromStopwatch(m_nStart, m_nStop)
, регулировать ticks
по мере необходимости.
uint32_t m_nStart; //DEBUG Stopwatch start cycle counter value
uint32_t m_nStop; //DEBUG Stopwatch stop cycle counter value
#define DEMCR_TRCENA 0x01000000
/* Core Debug registers */
#define DEMCR (*((volatile uint32_t *)0xE000EDFC))
#define DWT_CTRL (*(volatile uint32_t *)0xe0001000)
#define CYCCNTENA (1<<0)
#define DWT_CYCCNT ((volatile uint32_t *)0xE0001004)
#define CPU_CYCLES *DWT_CYCCNT
#define STOPWATCH_START { m_nStart = *((volatile unsigned int *)0xE0001004);}
#define STOPWATCH_STOP { m_nStop = *((volatile unsigned int *)0xE0001004);}
void main(void)
{
int timeDiff = 0;
stopwatch_reset();
STOPWATCH_START;
run_my_function();
STOPWATCH_STOP;
timeDiff = CalcNanosecondsFromStopwatch(m_nStart, m_nStop);
printf("My function took %d nanoseconds\n", timeDiff);
}
static inline void stopwatch_reset(void)
{
/* Enable DWT */
DEMCR |= DEMCR_TRCENA;
*DWT_CYCCNT = 0;
/* Enable CPU cycle counter */
DWT_CTRL |= CYCCNTENA;
}
static inline uint32_t stopwatch_getticks()
{
return CPU_CYCLES;
}
static inline void stopwatch_delay(uint32_t ticks)
{
uint32_t end_ticks = ticks + stopwatch_getticks();
while(1)
{
if (stopwatch_getticks() >= end_ticks)
break;
}
}
uint32_t CalcNanosecondsFromStopwatch(uint32_t nStart, uint32_t nStop)
{
uint32_t nDiffTicks;
uint32_t nClkTicksPerMicrosec;
nDiffTicks = nStop - nStart;
nDiffTicks *= 1000; // Scale diff by 1000.
nClkTicksPerMicrosec = SystemCoreClock / 1000000; // Convert (clkTicks/sec) to (clkTicks/microsec), SystemCoreClock = 168000000
return nDiffTicks / nClkTicksPerMicrosec; // nanosec = (ticks * 1000) / (clkTicks/microsec)
}
Это зависит от вашей реализации ARM.
Я использовал SysTick->VAL
зарегистрируйтесь на ядре stm32F4. Это точный цикл.
При интерпретации результатов позаботьтесь о:
- принять упаковку во внимание.
- Отсчитывается, а не вверх.
Ограничение: это работает только на интервалах, меньших, чем один шприц.