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

时间:2015-09-16 13:29:38

标签: c arm performancecounter cortex-m3 cortex-m

我想计算在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(link)执行的指令数量:

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

他们提到的寄存器记录在here(第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寄存器用于启用计数器,尤其是循环计数器,如文档here所述。

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

Here有一个关于如何在gdb中使用它们的小指南。

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

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

(gdb) watch *0xE0001014

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

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

谢谢!

3 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:1)

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

确保在SysTick Control and Status Register启用计数器。使用适当的标头,您应该可以访问SysTick寄存器作为结构。

测量计数器功能所采用的周期数。随后从任何测量值中减去。

  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;

现在测量一个功能。

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;

我希望它有所帮助。

答案 2 :(得分:1)

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

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

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

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

ITM? ETM?的CoreSight? DWT?AHB?