Счетчик циклов на 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. Это точный цикл.

При интерпретации результатов позаботьтесь о:

  • принять упаковку во внимание.
  • Отсчитывается, а не вверх.

Ограничение: это работает только на интервалах, меньших, чем один шприц.

Другие вопросы по тегам