[综合信息] MCU上代码执行时间

[复制链接]
 楼主| yeates333 发表于 2025-3-24 09:05 | 显示全部楼层 |阅读模式
(一)测量代码的执行时间  测量代码执行时间的方法有很多。作为一个嵌入式工程师,经常使用一个或多个数字输出和一个示波器。需要在执行要监视的代码之前设置一个高的输出,然后将输出降低。当然,在做这些之前有相当多的设置工作: 找到一个或多个自由输出,确保它们可以轻松访问,将端口配置为输出,编写代码,编译,设置范围等等。一旦有了一个信号,你可能需要对它进行一段时间的监视,以便看到最小值和最大值。 数字存储示波器使这个过程更容易,但是还有其他更简单的方法。
  另一种测量执行时间的方法是使用可跟踪调试接口。只需要运行代码,查看跟踪,计算 delta时间(通常是手动的) ,并将CPU周期转换为微秒。不幸的是,这个跟踪给了一个执行的实例,可能不得不在追踪捕获中进一步查找最坏情况下的执行时间。这是一个乏味的过程。


(二)Cortex-M 周期计数器在大多数Cortex-M的处理器中调试端口包含一个32位的自由运行计数器,它可以计算 CPU 的时钟周期。计数器是 Debug 观察和跟踪(DWT)模块的一部分,可以很容易地用于测量代码的执行时间。下面的代码是启用和初始化这个特性非常有用。

  1. #define  ARM_CM_DEMCR      (*(uint32_t *)0xE000EDFC)

  2. #define  ARM_CM_DWT_CTRL   (*(uint32_t *)0xE0001000)

  3. #define  ARM_CM_DWT_CYCCNT (*(uint32_t *)0xE0001004)


  4. if (ARM_CM_DWT_CTRL != 0) {        // See if DWT is available

  5.     ARM_CM_DEMCR      |= 1 << 24;  // Set bit 24

  6.     ARM_CM_DWT_CYCCNT  = 0;

  7.     ARM_CM_DWT_CTRL   |= 1 << 0;   // Set bit 0
  8. }


(三)使用DWT周期计数器来测量代码执行时间可以通过在目标代码之前和之后读取周期计数器的值来测量和计算代码段的执行时间,如下所示。 当然,这意味着必须设置代码,但能够得到一个非常准确的值。

  1. 1 uint32_t  start;
  2. 2  
  3. 3 uint32_t  stop;
  4. 4  
  5. 5 uint32_t  delta;
  6. 6  
  7. 7  
  8. 8 start = ARM_CM_DWT_CYCCNT;
  9. 9  
  10. 10 // Code to measure
  11. 11  
  12. 12 stop  = ARM_CM_DWT_CYCCNT;
  13. 13  
  14. 14 delta = stop – start;


因为使用的是无符号运算,delta表示所测量代码的实际执行时间(CPU 时钟周期)。
在测量开始和停止读数之间的代码执行时间时,可能会发生中断,所以每次执行这个序列很可能会有不同的值。在这种情况下,可能希望在测量过程中禁用中断,但是要清楚禁用中断是暂时的,只用于测量。尽管如此,也许应该把中断的任务包括进来,因为它们会影响到代码的最后执行时间。

  1. 1 Disable Interrupts;
  2. 2  
  3. 3 start = ARM_CM_DWT_CYCCNT;
  4. 4  
  5. 5 // Code to measure
  6. 6  
  7. 7 stop  = ARM_CM_DWT_CYCCNT;
  8. 8  
  9. 9 Enable Interrupts;
  10. 10  
  11. 11 delta = stop – start;


如果所测代码包含条件语句、循环或任何可能导致变化的东西,那么获得的值可能不代表最坏情况下的执行时间。为了纠正这个问题,需要添加一个峰值检测器,如下图所示。当然,在进行任何测量之前,需要将 max 声明并初始化为最小值(即0)。

  1. 1 start = ARM_CM_DWT_CYCCNT;
  2. 2  
  3. 3 // Code to measure
  4. 4  
  5. 5 stop  = ARM_CM_DWT_CYCCNT;
  6. 6  
  7. 7 delta = stop – start;
  8. 8  
  9. 9 if (max < delta) {
  10. 10  
  11. 11     max = delta;
  12. 12  
  13. 13 }


同样,知道最短执行时间也是有趣且有用的 在进行任何测量之前,只需要声明和初始化最大可能值(即0xFFFFFFFF)。下面是新的代码:

  1. 1 tart = ARMCMDWT_CYCCNT;
  2. 2
  3. 3 // Code to measure
  4. 4
  5. 5 stop = ARMCMDWT_CYCCNT;
  6. 6
  7. 7 delta = stop – start;
  8. 8
  9. 9 if (max < delta) {
  10. 10
  11. 11 max = delta;
  12. 12 }
  13. 13
  14. 14 if (min > delta) {
  15. 15
  16. 16 min = delta;
  17. 17 }


就像 Cortex-M4处理器和 Cortex-M7那样,执行时间还取决于CPU是否配备了缓存。如果系统中使用了指令或数据缓存,对同一段代码的多重测量可能不一致。这时,可以考虑禁用缓存以测量最坏的情况。
大多数调试器允许显示这些变量值。如果是这样,则需要在全局范围内声明显示变量,以保留它们的值并允许实时监控。不幸的是,这些值代表的是CPU时钟周期,而且大多数调试器还不够成熟,无法为了显示目的而对变量进行缩放。假设一个16兆赫的CPU时钟速度,显示70.19微秒比显示1123个周期要方便得多。实际上还有一种更好的方法来显示这些变量,这也提供了规模化能力,可以以一种更加可读的形式看待它们。


(四)经过的时间模块当然,可以将代码片段嵌入到应用程序中,但还可以可以使用一个简单的模块。 elapsedtime.c与elapsedtime.h,它仅由4个函数组成。
方法如下:
(1)按照惯例,#include
(2)在使用elapsedtime.c 中的其他函数之前,调用 elapsedtime_init()
(3)通过设置"ELAPSEDTIMEMAX_SECTIONS"来定义时间测量结构的最大数目。这与用 stop/start代码包装的不同代码段相对应
(4)调用elapsedtimestart()并传递要监视的代码片段的索引(即0 到ELAPSEDTIMEMAX_SECTIONS-1)
(5)调用elapsedtimestop()并传递在运行时启动时所使用的相同索引
(6)如果调试器允许监视变量(即当目标正在运行时) ,则可以显示elapsedtimetbl[],并查看对应索引的运行时间结构
(7)重复执行步骤4到6,并将代码置于最坏和最好的情况下,以便ELAPSED_TIME数据结构中的Min 和max 字段可以很好地表示所测量代码片段的执行时间


需要注意的是, 没有在测量过程中禁用中断,因为ISR可能会涉及到,也需要了解这会如何影响感知的执行时间。


  1. 1 void  main (void)
  2. 2 {
  3. 3  
  4. 4     // Some code
  5. 5  
  6. 6     elapsed_time_init();         // Iitialize the module
  7. 7  
  8. 8     // Some code
  9. 9  
  10. 10 }
  11. 11  
  12. 12 void  MyCode (void)
  13. 13 {
  14. 14  
  15. 15     // Some code here
  16. 16  
  17. 17     elapsed_time_start(0);    // Start measurement of code snippet #0
  18. 18  
  19. 19     // Code being measured
  20. 20  
  21. 21     elapsed_time_stop(0);     // Stop and
  22. 22  
  23. 23     // Some other code
  24. 24  
  25. 25 }


当然,最小和最大的执行时间取决于测量的频率,以及代码是否分别受到最佳和最差条件的限制。
另外,没有必要显示起始字段,因为它只用于在测量开始时记录DWT周期计数器的值,然而,启动字段可以用来显示出来。换句话说,当看到这个值变化时,就会知道测量正在发生。

(五)使用 uc / probe 的示例显示使用了elapsed_time.c 和 uc/probe,来测量一下代码片段的执行时间。



  图1| IAR 和 uc/probe 的树视图
  图1显示了使用IAR的LiveWatch (左)和 uc / probe 的树视图(右)。截图是在不同的时间拍摄的,是一个存储不同代码片段的测量值的数组。
可以将min/max/current分配给计量表和数字指示器,如图2所示。CPU 运行在80mhz,这些值以微秒显示,应用了0.0125的缩放因子。左侧的按钮用于重置统计数据,从而迫使重新计算最小值和最大值。




  图2 | 使用uc/probe 的仪表显示最大执行时间
  Uc/probe 的一个强大特性是能够与微软的 Excel 对接,从而在电子表格中显示这些值(实时) ,如图3所示。


  图3 | 使用 Excel 显示实时数据

附录代码elapsed_time.c

  1. 1 #include  <stdint.h>
  2.   2 #include  <elapsed_time.h>
  3.   3  
  4.   4 /*
  5.   5 ********************************************************************************
  6.   6 *                           CORTEX-M - DWT TIMER
  7.   7 ********************************************************************************
  8.   8 */
  9.   9  
  10. 10 #define  ARM_CM_DEMCR      (*(uint32_t *)0xE000EDFC)
  11. 11 #define  ARM_CM_DWT_CTRL   (*(uint32_t *)0xE0001000)
  12. 12 #define  ARM_CM_DWT_CYCCNT (*(uint32_t *)0xE0001004)
  13. 13  
  14. 14 /*
  15. 15 ********************************************************************************
  16. 16 *                             Data Structure
  17. 17 ********************************************************************************
  18. 18 */
  19. 19  
  20. 20 typedef  struct  elapsed_time {
  21. 21     uint32_t  start;
  22. 22     uint32_t  current;
  23. 23     uint32_t  max;
  24. 24     uint32_t  min;
  25. 25 } ELAPSED_TIME;
  26. 26  
  27. 27 /*
  28. 28 ********************************************************************************
  29. 29 *                      STORAGE FOR ELAPSED TIME MEASUREMENTS
  30. 30 ********************************************************************************
  31. 31 */
  32. 32  
  33. 33 static  ELAPSED_TIME  elapsed_time_tbl[ELAPSED_TIME_MAX_SECTIONS];
  34. 34  
  35. 35 /*
  36. 36 ********************************************************************************
  37. 37 *                              MODULE INITIALIZATION
  38. 38 *
  39. 39 * Note(s): Must be called before any of the other functions in this module
  40. 40 ********************************************************************************
  41. 41 */
  42. 42  
  43. 43 void  elapsed_time_init (void)         
  44. 44 {
  45. 45     uint32_t  i;
  46. 46     
  47. 47     
  48. 48     if (ARM_CM_DWT_CTRL != 0) {                  // See if DWT is available
  49. 49         ARM_CM_DEMCR      |= 1 << 24;            // Set bit 24
  50. 50         ARM_CM_DWT_CYCCNT  = 0;               
  51. 51         ARM_CM_DWT_CTRL   |= 1 << 0;             // Set bit 0
  52. 52     }
  53. 53     for (i = 0; i < ELAPSED_TIME_MAX_SECTIONS; i++) {
  54. 54         elapsed_time_clr(i);
  55. 55     }
  56. 56 }
  57. 57  
  58. 58 /*
  59. 59 ********************************************************************************
  60. 60 *                  START THE MEASUREMENT OF A CODE SECTION
  61. 61 ********************************************************************************
  62. 62 */
  63. 63  
  64. 64 void  elapsed_time_start (uint32_t  i)  
  65. 65 {
  66. 66     elapsed_time_tbl[i].start = ARM_CM_DWT_CYCCNT;
  67. 67 }
  68. 68  
  69. 69 /*
  70. 70 ********************************************************************************
  71. 71 *           STOP THE MEASUREMENT OF A CODE SECTION AND COMPUTE STATS
  72. 72 ********************************************************************************
  73. 73 */
  74. 74  
  75. 75 void  elapsed_time_stop (uint32_t  i)  
  76. 76 {
  77. 77     uint32_t       stop;
  78. 78     ELAPSED_TIME  *p_tbl;
  79. 79     
  80. 80  
  81. 81     stop           = ARM_CM_DWT_CYCCNT;   
  82. 82     p_tbl          = &elapsed_time_tbl[i];
  83. 83     p_tbl->current = stop - p_tbl->start;
  84. 84     if (p_tbl->max < p_tbl->current) {
  85. 85         p_tbl->max = p_tbl->current;
  86. 86     }
  87. 87     if (p_tbl->min > p_tbl->current) {
  88. 88         p_tbl->min = p_tbl->current;
  89. 89     }
  90. 90 }
  91. 91  
  92. 92 /*
  93. 93 ********************************************************************************
  94. 94 *                      CLEAR THE MEASUREMENTS STATS
  95. 95 ********************************************************************************
  96. 96 */
  97. 97  
  98. 98 void  elapsed_time_clr (uint32_t  i)         
  99. 99 {
  100. 100     ELAPSED_TIME  *p_tbl;
  101. 101     
  102. 102     
  103. 103     p_tbl          = &elapsed_time_tbl[i];
  104. 104     p_tbl->start   = 0;
  105. 105     p_tbl->current = 0;
  106. 106     p_tbl->min     = 0xFFFFFFFF;
  107. 107     p_tbl->max     = 0;
  108. 108 }

  109. elapsed_time.h

  110. 1 /*
  111. 2 ********************************************************************************
  112. 3 *                       MODULE TO MEASURE EXECUTION TIME
  113. 4 ********************************************************************************
  114. 5 */
  115. 6  
  116. 7 /*
  117. 8 ********************************************************************************
  118. 9 *                MAXIMUM NUMBER OF ELAPSED TIME MEASUREMENT SECTIONS
  119. 10 ********************************************************************************
  120. 11 */
  121. 12  
  122. 13 #define  ELAPSED_TIME_MAX_SECTIONS  10
  123. 14  
  124. 15 /*
  125. 16 ********************************************************************************
  126. 17 *                             FUNCTION PROTOTYPES
  127. 18 ********************************************************************************
  128. 19 */
  129. 20  
  130. 21 void  elapsed_time_clr   (uint32_t  i);      // Clear measured values
  131. 22 void  elapsed_time_init  (void);             // Module initialization
  132. 23 void  elapsed_time_start (uint32_t  i);      // Start measurement
  133. 24 void  elapsed_time_stop  (uint32_t  i);      // Stop  measurement


参考文献
https://www.micrium.com/ucprobe/about/
https://www.iar.com/iar-embedded-workbench/
https://www.arm.com/products/processors/cortex-m

本文链接:https://blog.csdn.net/wireless_com/article/details/81040007

szt1993 发表于 2025-7-31 22:44 | 显示全部楼层
代码执行时间有必要测量一下
您需要登录后才可以回帖 登录 | 注册

本版积分规则

20

主题

1531

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部