Счетчик циклов на ARM Cortex M4 (или M3)?

Я пытаюсь профилировать функцию C (которая вызывается из прерывания, но я могу ее извлечь и профилировать в другом месте) на Cortex M4.

Каковы возможности подсчета количества циклов, обычно используемых в этой функции? Функция должна работать в ~ 4000 циклов вверх, поэтому RTC не является вариантом, я думаю, и вручную подсчет циклов от разборки может быть болезненным - и полезен только при усреднении, потому что я хотел бы профилировать в типичном потоке с типичной флэш-памятью шаблон использования.

Я слышал о регистрах счетчиков циклов и инструкциях MRC, но они, похоже, доступны для A8/11. Я не видел таких инструкций в микроскопе cortex-Mx.

Ответ 1

Посмотрите на регистр DWT_CYCCNT, указанный здесь здесь. Обратите внимание, что этот регистр зависит от реализации. Кто является продавцом чипов? Я знаю, что реализация STM32 предлагает этот набор регистров.

Этот пост содержит инструкции по использованию регистра счетчика циклов DWT для синхронизации. (См. Форму сообщения 11 декабря 2009 г. - 18:29)

Этот пост является примером того, как DWT_CYCCNT также.

Ответ 2

Если ваша часть включает CoreSight Embedded Trace Macrocell, и у вас есть подходящее аппаратное и программное обеспечение для отладки, то вы можете напрямую профилировать код. Трассирующее отладочное оборудование, конечно же, более дорогое, и ваша плата должна быть спроектирована так, чтобы сделать выводки портов трассировки доступными для заголовка отладки. Поскольку эти контакты часто мультиплексируются с другими функциями, это может быть не всегда возможно или практично.

В противном случае, если ваша цепочка инструментов включает в себя точный симулятор (такой как доступный в Keil uVision), вы можете использовать его для анализа время синхронизации кода. Симулятор обеспечивает функции отладки, трассировки и профилирования, которые, как правило, более мощные и гибкие, чем те, которые доступны на чипе, поэтому, даже если у вас есть оборудование для трассировки, симулятор по-прежнему может быть более простым решением.

Ответ 3

Это проще:

[код]

#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 и просто подсчитывает требуемые циклы.

Ответ 4

Это зависит от реализации ARM.

Я использовал регистр SysTick->VAL в ядре stm32F4. Это точность цикла.

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

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

Ограничение: Это работает только с интервалами, меньшими, чем один систол.

Ответ 5

Расширение предыдущих ответов с помощью примера 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)
}