ARM Cortex M4(或M3)上的循环计数器?

时间:2012-07-17 20:44:02

标签: embedded arm cortex-m3

我正试图在Cortex M4上分析一个C函数(从中断调用,但我可以在其他地方提取它并将其分析)。

有多少可能计算此功能中通常使用的周期数? 功能应该在大约4000个周期内运行,所以我认为RTC不是一个选项,并且从反汇编中手动计算周期可能很痛苦 - 只有平均才有用,因为我想在具有典型闪存/内存的典型流上进行分析使用模式。

我听说过循环计数器寄存器和MRC指令,但它们似乎可用于A8 / 11。我没有在cortex-Mx micros中看到过这样的说明。

5 个答案:

答案 0 :(得分:17)

查看定义的here DWT_CYCCNT寄存器。请注意,此寄存器与实现有关。谁是芯片供应商?我知道STM32实现提供了这组寄存器。

This post提供了使用DWT循环计数器寄存器进行定时的说明。 (见2009年12月11日 - 下午06:29发表的表格)

This Stack overflow post也是关于如何使用DWT_CYCCNT的示例。

答案 1 :(得分:3)

如果您的部件包含CoreSight Embedded Trace Macrocell并且您具有适当的跟踪调试器硬件和软件,那么您可以直接分析代码。具有跟踪功能的调试硬件当然更昂贵,并且您的电路板需要设计为使调试头上的跟踪端口引脚可用。由于这些引脚通常与其他功能复用,因此可能并非总是可行或不实用。

否则,如果您的工具链包含一个周期精确的模拟器(例如Keil uVision中提供的模拟器),您可以使用它来分析代码时序。模拟器提供调试,跟踪和分析功能,这些功能通常比芯片上的功能更强大,更灵活,因此即使您有跟踪硬件,模拟器仍可能是更容易的解决方案。

答案 2 :(得分:1)

这更容易:

[代码]

#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
****/

[/代码]

适用于CJMCU板上的Cortex M4:STM32F407VGT,只计算所需的周期。

答案 3 :(得分:0)

这取决于您的ARM实现。

我在stm32F4核心上使用了SysTick->VAL寄存器。 这是周期准确的。

在解释结果时,请注意:

  • 考虑到包装。
  • 倒数,而不是起来。

限制:   这仅适用于小于单个指南针的间隔。

答案 4 :(得分:0)

使用main中的DWT_CYCCNT示例(STM32)扩展先前的答案(类似于我的other post)。

注意:我也添加了延迟方法。您可以致电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)
}