每个周期的ARM M4指令(IPC)计数器

我想计算在ARM cortex-M4(或cortex-M3)处理器上执行的每个周期的指令数。

它需要的是:我想要分析的代码的指令数量 (在运行时执行)以及代码执行的周期数

1 – 周期数

使用循环计数器非常简单直接。

volatile unsigned int *DWT_CYCCNT ; volatile unsigned int *DWT_CONTROL ; volatile unsigned int *SCB_DEMCR ; void reset_timer(){ DWT_CYCCNT = (int *)0xE0001004; //address of the register DWT_CONTROL = (int *)0xE0001000; //address of the register SCB_DEMCR = (int *)0xE000EDFC; //address of the register *SCB_DEMCR = *SCB_DEMCR | 0x01000000; *DWT_CYCCNT = 0; // reset the counter *DWT_CONTROL = 0; } void start_timer(){ *DWT_CONTROL = *DWT_CONTROL | 1 ; // enable the counter } void stop_timer(){ *DWT_CONTROL = *DWT_CONTROL | 0 ; // disable the counter } unsigned int getCycles(){ return *DWT_CYCCNT; } main(){ .... reset_timer(); //reset timer start_timer(); //start timer //Code to profile ... myFunction(); ... stop_timer(); //stop timer numCycles = getCycles(); //read number of cycles ... } 

2 – 指令数

我发现一些在互联网上冲浪的文件来计算arm cortex-M3和cortex-M4( 链接 )执行的指令数量:

  # instructions = CYCCNT - CPICNT - EXCCNT - SLEEPCNT - LSUCNT + FOLDCNT 

他们提到的寄存器在这里记录 (从第11-13页开始),这些是访问它们的内存地址:

 DWT_CYCCNT = 0xE0001004 DWT_CONTROL = 0xE0001000 SCB_DEMCR = 0xE000EDFC DWT_CPICNT = 0xE0001008 DWT_EXCCNT = 0xE000100C DWT_SLEEPCNT = 0xE0001010 DWT_LSUCNT = 0xE0001014 DWT_FOLDCNT = 0xE0001018 

DWT_CONTROL寄存器用于启用计数器,尤其是循环计数器,如此处所述 。

但是,当我试图将所有数据放在一起计算每个周期执行的指令数时,我没有成功。

这里有一个关于如何从gdb使用它们的小指南。

不容易的是,一些寄存器是8位寄存器(DWT_CPICNT,DWT_EXCCNT,DWT_SLEEPCNT,DWT_LSUCNT,DWT_FOLDCNT),当它们溢出时会触发事件。 我没有找到收集该事件的方法。 没有代码片段可以解释如何执行此操作或中断适合该操作的例程。

此外,在这些寄存器的地址上使用gdb的观察点似乎不起作用。 当寄存器改变值时,gdb无法停止。 例如在DWT_LSUCNT上:

 (gdb) watch *0xE0001014 

更新:我在GitHub上发现了这个项目 ,解释了如何使用DWT,ITM和ETM单元。 但我没有检查它是否有效! 我会发布更新。

有关如何使用它们的任何想法?

谢谢!

您提供的代码示例在清除启用位时存在问题。 你应该清楚地说’AND’而不是’OR’:

 *DWT_CONTROL = *DWT_CONTROL & 0xFFFFFFFE ; // disable the counter by clearing the enable bit 

我不知道如何以你想要的方式使用寄存器。 但是,这是我如何处理测量周期。

确保在SysTick控制和状态寄存器中启用计数器。 使用适当的标头,您应该可以访问SysTick寄存器作为结构。

测量计数器function所采用的周期数。 稍后从任何测量值中减去该值。

  SysTick->VAL = 0; // set 0 // Measure delay on measurement __disable_irq(); a = (uint32_t) SysTick->VAL; //... measuring zero instructions b = (uint32_t) SysTick->VAL; __enable_irq(); measure_delay = a - b; 

现在测量一个function。

 SysTick->VAL = 0; __disable_irq(); a = (uint32_t) SysTick->VAL; //Assuming this function doesn't require interruptions // INSERT CODE TO BE PROFILED function_to_be_examined(); b = (uint32_t) SysTick->VAL; __enable_irq(); cycles_profiled_code = a - b - measure_delay; 

我希望它有所帮助。

我想如果你想测量精度周期,使用调试器是一个不错的选择。 Keil-MDK可以累积状态寄存器并且不会溢出。 调试器中的结果与使用DWT的结果相同。

如果你想测量其他值,即FOLDCNT,使用Keil-MDK中的跟踪 – >调试 – >设置 – >跟踪 – >跟踪启用。

有了这个,在调试时,在Trace Windows中选择跟踪事件,这些8位寄存器的值可以由Keil收集并加在一起。

这似乎有点愚蠢,但我不知道如何收集溢出事件,我认为这个事件只能发送给ITM,因为DWT或ITM是程序之外的单个组件。 如果我们想在客户程序中收集事件,收集操作必须影响结果的准确性。

ITM? ETM? 的CoreSight? DWT?AHB?