ARM Cortex M4(或M3)上的周期计数器?

Posted

技术标签:

【中文标题】ARM Cortex M4(或M3)上的周期计数器?【英文标题】:Cycle counter on ARM Cortex M4 (or M3)? 【发布时间】:2012-07-16 20:27:12 【问题描述】:

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

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

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

【问题讨论】:

大多数微控制器都有定时器,cortex-m3 在内核中有一个(如果我没记错的话,m4 没有,或者 m0 没有两者之一)。 github.com/dwelch67 我有很多例子,都是从闪烁的 LED 开始逐步使用不同的计时器等。mbed 和 stm32f4d 是 cortex-m 的例子(还有其他例子)。 【参考方案1】:

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

This post 提供了使用 DWT 周期计数器寄存器进行计时的说明。 (请参阅 2009 年 12 月 11 日 - 下午 6:29 发布的表格)

This Stack overflow post 也是 DWT_CYCCNT 的示例。

【讨论】:

我很快就看到了,但认为它只是一个计数器的比较器,只能在每次给定值时产生中断。所以我只会有一个不精确的计数——每 500 个周期中断一次,或者对性能有很大影响,总是中断代码?如何获取或使用它的价值? (确实是STM32F4芯片) @makapuf:见编辑后的帖子。您应该能够使用此寄存器获得精确的时序。 将链接中的内容包含在他们再次死亡的答案案例中 作为后人的后续,这个链接相当不错:***.com/questions/13379220/… “这篇文章”链接失效【参考方案2】:

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

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

【讨论】:

我在Linux上使用gnu工具链,所以gcc/gdb 一个稍微复杂的解决方案可能是使用 Windows 机器或在 VirtualBox 中运行的 Windows VM,然后使用带有 Codesourcery 的 GNU ARM 工具链的 Keil uVision 评估版。评估限制在 ARM RealView 编译器/链接器而不是 IDE 上,我不确定调试器/模拟器,但即使它们受到限制,代码大小限制为 32k,因此如果不是整个应用程序,您可能可以测试此功能.详情:keil.com/appnotes/docs/apnt_199.asp。不过可能会很麻烦。 谢谢,但这只是一个模拟,基于一个完美的内存模型(作为第一个近似值可能很好,但如果内存总线争用,我会更好地相信真实的交易(我大量使用DMA 传输也...) @makapuf:没错,但同样,在这种情况下,您可能永远不会知道您的“真实”测量是否代表最坏的情况。实际测量值将是可变的,而模拟将为您提供一个基线常数,从中可以计算出最坏的情况(也许)。两者都做会很有趣,但你可能没有时间或设备。我建议 Throwback1986 的解决方案。 我也想从它开始。再次感谢您的回答。另外,说到模拟,看来ARMulator是一个循环完美的ARM模拟器,你有没有经验?【参考方案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 板上,只计算所需的周期。

【讨论】:

适用于 MK22FN512xxx12【参考方案4】:

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

注意:我也添加了延迟方法。您可以通过调用STOPWATCH_START 来验证stopwatch_delay,运行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 CLK_SPEED         168000000 // EXAMPLE for CortexM4, EDIT as needed

#define STOPWATCH_START  m_nStart = *((volatile unsigned int *)0xE0001004);
#define STOPWATCH_STOP   m_nStop = *((volatile unsigned int *)0xE0001004);


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 nSystemCoreTicksPerMicrosec;

    // Convert (clk speed per sec) to (clk speed per microsec)
    nSystemCoreTicksPerMicrosec = CLK_SPEED / 1000000;

    // Elapsed ticks
    nDiffTicks = nStop - nStart;

    // Elapsed nanosec = 1000 * (ticks-elapsed / clock-ticks in a microsec)
    return 1000 * nDiffTicks / nSystemCoreTicksPerMicrosec;
 

void main(void)

    int timeDiff = 0;
    stopwatch_reset();

    // =============================================
    // Example: use a delay, and measure how long it took
    STOPWATCH_START;
    stopwatch_delay(168000); // 168k ticks is 1ms for 168MHz core
    STOPWATCH_STOP;

    timeDiff = CalcNanosecondsFromStopwatch(m_nStart, m_nStop);
    printf("My delay measured to be %d nanoseconds\n", timeDiff);

    // =============================================
    // Example: measure function duration in nanosec
    STOPWATCH_START;
    // run_my_function() => do something here
    STOPWATCH_STOP;

    timeDiff = CalcNanosecondsFromStopwatch(m_nStart, m_nStop);
    printf("My function took %d nanoseconds\n", timeDiff);

【讨论】:

【参考方案5】:

这取决于你的 ARM 实现。

我在 stm32F4 内核上使用了 SysTick-&gt;VAL 寄存器。 这是循环准确的。

在解释结果时,请注意:

考虑换行。 倒计时,而不是倒计时。

限制: 这仅适用于小于单个 systick 的间隔。

【讨论】:

以上是关于ARM Cortex M4(或M3)上的周期计数器?的主要内容,如果未能解决你的问题,请参考以下文章

ARM

你能帮我理解 ARM Cortex-A9 上的缓存行为吗?

如何获得可靠的 Cortex M4 短延迟

ARM Cortex M3上的GCC:从特定地址调用函数

带有freertos的gnu arm cortex m4上的C ++异常处理程序

优化 ARM Cortex M3 代码