我正在尝试在 Cortex M4 上分析 C 函数(从中断调用,但我可以提取它并在其他地方分析)。
计算此函数中通常使用的循环次数的可能性有哪些? 函数应在 ~4000 个周期内运行,所以我猜 RTC 不是一个选项,并且手动计算反汇编的周期可能很痛苦 - 并且只有在取平均值时才有用,因为我想用典型的闪存/内存对典型的流进行分析使用模式。
听说过循环计数寄存器和MRC指令,不过A8/11好像都有。我没有在cortex-Mx micros中看到这样的指令。
查看here定义的DWT_CYCCNT寄存器。请注意,此寄存器是依赖于实现的。谁是芯片供应商?我知道 STM32 实现提供了这组寄存器。
这篇文章 提供了使用 DWT 循环计数器寄存器进行计时的说明。 (参见 2009 年 12 月 11 日 - 下午 6:29 的帖子表格)
这个 Stack overflow post 也是一个关于如何 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 上工作:CJMCU 板上的 STM32F407VGT,只计算所需的周期。
使用
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 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);
}
这取决于您的 ARM 实现。
我在stm32F4内核上使用了
SysTick->VAL
寄存器。
这是周期准确的。
解释结果时,注意:
限制: 这仅适用于 大于 的间隔,而不是单个系统。