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->VAL
寄存器。
这是循环准确的。
在解释结果时,请注意:
考虑换行。 倒计时,而不是倒计时。限制: 这仅适用于小于单个 systick 的间隔。
【讨论】:
以上是关于ARM Cortex M4(或M3)上的周期计数器?的主要内容,如果未能解决你的问题,请参考以下文章