【问题标题】:ARM M4 Instructions per Cycle (IPC) countersARM M4 每周期指令 (IPC) 计数器
【发布时间】:2015-12-13 02:58:25
【问题描述】:

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

需要的是:我要分析的代码的指令数(在运行时执行)以及代码执行所需的周期数。 p>

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),当它们溢出时会触发事件。我没有找到收集该事件的方法。没有代码 sn-p 解释如何执行此操作或中断适用于此的例程。

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

(gdb) watch *0xE0001014

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

知道如何使用它们吗?

谢谢!

【问题讨论】:

  • 也许太明显了,但你总是在执行任何其他函数之前调用 reset_timer(),对吗?您能否发布调用代码,作为一个最小示例?
  • 我建议将寄存器声明为#define DWT_CYCCNT (*(volatile uint32_t*)0xE0001004ul)
  • 事件不是会触发调试监视器异常的调试事件吗?
  • 我不知道会生成什么样的事件。在 arm docs link 他们谈论事件但我没有找到任何方法来收集它们
  • *DWT_CONTROL = *DWT_CONTROL | 0 ; // disable the counter 无操作

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


【解决方案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;

希望对你有帮助。

【讨论】:

  • 请注意,此测量的粒度非常粗略,因为 SysTick “通常”设置为溢出,例如1毫秒。问题中的代码测量精确的周期计数。
  • 注意到了。正确的。粗略地说,如果处理器速度为 100MHz,则 1ms 将允许测量大约 100,000 个周期。这种方法对于需要上万次循环的函数是精确的。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

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

    如果你想测量其他值,即FOLDCNT,使用Keil-MDK中的trace -> Debug -> Setting -> Trace -> Trace Enable。

    这样,在调试时,在Trace窗口中选择trace事件,这8位寄存器的值就可以被Keil收集起来了。

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

    ITM? ETM?核心视力?载重吨?AHB?

    【讨论】:

    • 你知道如何在不影响程序执行的情况下收集计数器溢出事件吗?
    猜你喜欢
    • 2012-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-28
    • 1970-01-01
    • 1970-01-01
    • 2021-07-23
    相关资源
    最近更新 更多