打印

我的 STM32 到底跑多快?

[复制链接]
4941|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
airwill|  楼主 | 2010-2-5 09:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
我的 STM32 到底跑多快?
我在 STM32F103RB 的开发板上, 写如下代码:

  uint32_t x = 0x009CF260, y;
  while (1)  {
   if (--x ==0) {
  x = 0x009CF260;
  y = 1;
  if(GPIOA->IDR & 1) y <<= 16;
  GPIOA->BSRR = y;
}
  }
为什么  x = 0x009CF260? 这是推算出来的, 目的是1秒钟翻转一次 PA0.
怎么推算的呢?
  if (SysTick_Config(SystemFrequency / 8)) { while(1);}
  设定 1/8 秒一次 SysTick 中断. 由调试器读取寄存器的值.
  0x008961ae,0x0075c362,0x00622516...
             0x00139E4C,0x00139E4C...
             0x00139E4C * 8 = 0x009CF260
那么在  0x00139E4C 个循环里, 到底执行了多少条指令呢? 看下面的编译结果           
;;;72       while (1)  {
;;;73        if (--x ==0) {
;;;74       x = 0x009CF260;
;;;75       y = 1;
;;;76       if(GPIOA->IDR & 1) y <<= 16;
000070  4909              LDR      r1,|L1.152|
000072  4625              MOV      r5,r4                 ;53
000074  f44f3280          MOV      r2,#0x10000
000078  e000              B        |L1.124|
                  |L1.122|
00007a  e7fe              B        |L1.122|
                  |L1.124|
00007c  1e64              SUBS     r4,r4,#1              ;73
00007e  d1fd              BNE      |L1.124|
000080  f8d13808          LDR      r3,[r1,#0x808]
000084  462c              MOV      r4,r5                 ;74
000086  2001              MOVS     r0,#1                 ;75
000088  07db              LSLS     r3,r3,#31
00008a  d000              BEQ      |L1.142|
00008c  4610              MOV      r0,r2
                  |L1.142|
;;;77       GPIOA->BSRR = y;
00008e  f8c10810          STR      r0,[r1,#0x810]
000092  e7f3              B        |L1.124|
;;;78      }
;;;79       }
;;;80     }
    分析: 从上面的编译结果, 可以看到, 变量 x 被分配给寄存器 R4,
在标号 |L1.124| 后面两条指令, 就执行了 减1 不为零, 再循环的任务.
也就是说, 1 秒钟, 就执行了 0x009CF260 = 10285664 个循环.
    通过 SysTick_Config 函数, 可以肯定, 现在的执行频率是 72MHz.
据此推算, 这两条指令的循环需要 7 个周期,
也就是说, 后面的分支跳转指令需要 6 个周期?  有这么慢?
我的 JLINK V8 没有告诉我!
沙发
香水城| | 2010-2-5 09:57 | 只看该作者
这个计算需要考虑流水线和Flash的插入等待周期效应。

使用特权

评论回复
板凳
airwill|  楼主 | 2010-2-5 13:47 | 只看该作者
发现更为奇怪的结果了: 看下面的修改的代码
  while (1)  {
          if ((--x)&&(--x ==0)) {
                x = SPEED1S;                 
                y = 1;
                if(GPIOA->IDR & 1) y *= 0x10000;
                GPIOA->BSRR = y;
        }
  }
  
;;;75       while (1)  {
;;;76               if ((--x)&&(--x ==0)) {
;;;77                     x = SPEED1S;                 
;;;81                     y = 1;
;;;82                     if(GPIOA->IDR & 1) y *= 0x10000;
000072  490b              LDR      r1,|L1.160|
000074  4625              MOV      r5,r4                 ;54
000076  f44f3280          MOV      r2,#0x10000
00007a  e000              B        |L1.126|
                  |L1.124|
00007c  e7fe              B        |L1.124|
                  |L1.126|
00007e  1e64              SUBS     r4,r4,#1              ;76
000080  d0fd              BEQ      |L1.126|
000082  1e64              SUBS     r4,r4,#1              ;76
000084  d1fb              BNE      |L1.126|
000086  f8d13808          LDR      r3,[r1,#0x808]
00008a  462c              MOV      r4,r5                 ;77
00008c  2001              MOVS     r0,#1                 ;81
00008e  07db              LSLS     r3,r3,#31
000090  d000              BEQ      |L1.148|
000092  4610              MOV      r0,r2
                  |L1.148|
;;;83                     GPIOA->BSRR = y;
000094  f8c10810          STR      r0,[r1,#0x810]
000098  e7f1              B        |L1.126|
;;;84             }
;;;85       }

    分析: 从上面的编译结果, 可以看到, 变量 x 仍被分配给寄存器 R4,
在标号 |L1.126| 后面四条指令, 就执行了两次减1 后判断的循环任务.
这两个分支, 一个不需要跳转, 另一个需要跳转. 接下来看测试的结果.
下面是每次 Systick 中断中读取到的 x(R4) 的值.

  0x0075c35e,0x004e86c6,0x00274a2e,0x00000d96
差值       ,0x00273c98,0x00273c98,0x00273c98

也就是说, 1 秒钟, 执行了 0x273c98 * 4 = 0x9CF260 个循环.
对, 仍然是 0x9CF260 , 写这里的时候, 我反复过 n 次了.
有人要说了,  1/8 秒不是要乘以 8 嘛, 但是一次循环里执行了两次减1.

据此推算, 这四条指令的循环一次需要 7 个周期,
难道说减法指令和分支不跳转不需要时间, 光那条分支后跳转指令需要 7 个周期?

使用特权

评论回复
地板
香水城| | 2010-2-5 14:35 | 只看该作者
分支后跳转指令需要清空流水线和重新加载流水线,重新加载流水线需要读Flash,这些都需要时间。

使用特权

评论回复
5
txcy| | 2010-2-5 20:56 | 只看该作者
主要是看你的程序优化的效率

使用特权

评论回复
6
xsgy123| | 2010-2-6 14:40 | 只看该作者
在处理复杂的算法上好像没啥优势

使用特权

评论回复
7
yybj| | 2010-2-6 19:58 | 只看该作者
不能这么测速度吧

使用特权

评论回复
8
司徒老鹰| | 2010-2-6 23:47 | 只看该作者
学到新东西了……

使用特权

评论回复
9
gxgclg| | 2010-2-7 12:59 | 只看该作者
速度够用就行了,一般对速度的要求没那么高,再说ST的速度已经很快了

使用特权

评论回复
10
airwill|  楼主 | 2010-2-8 10:27 | 只看该作者
看样子, 调试器通信是需要时间的. 问题一定在调试器上面;
为此, 重新修改 Systick 中断服务程序, 将上面的计数器 x 记录下来保存到内存,
若干次后看保存的结果. 相信这样能脱离调试器的影响了吧.
哦, 获取寄存器的值是 C 所难办到的事情, 我使用下面的汇编代码来完成.

__inline __asm  unsigned long getdat(void) {
        mov r0, R4;
        bx         lr;
}  

下面是 Systick 中断服务程序, 取 4 次的值, 然后在把断点放在 改变 PA1 的地方.

volatile unsigned char ucnt;  
unsigned long ldat[16];
void SysTick_Handler(void) {
        unsigned long cnt, dat = getdat();
        unsigned long dsp = 2;
        cnt = ucnt;
        ldat[cnt] = dat;
        cnt = (ucnt +1)%4; ucnt = cnt;
        if (cnt ==0) {
                if(GPIOA->IDR&2) dsp <<= 16;
                GPIOA->BSRR = dsp;
        }
}

    下面是记录到的连续 4 个数据.
  0x861CA0,0x6F394B,0x5855F5,0x41729F
差值      0x16E355,0x16E356,0x16E356  * 8 = B71AB0  (11999920)

这样的结果应该比较真实.两条指令共 6 个周期, 分支跳转使用 5 个周期.      
    下面是使用后面的代码 (四条指令一个循环) 记录到的连续 4 个数据.
      
  0x7E7B80,0x5FF70E,0x323062,0x13ABF0
差值      0x1E8472,0x2DC6AC,0x1E8472  * 4 = 7A11C8  (7999944)
      
     这里有个过分超差的值, 剩余 2 个是比较合理, 计算结果为 9 个周期.
估计应该是运算指令 1 个周期, 分支跳转 5 个周期, 分支不跳转 2 个周期.      
     另外发现一个过分超差的值, 经过多次调试, 发现会若干次后出现一次.
会不会就是前面提到的调试器的影响呢?

使用特权

评论回复
11
香水城| | 2010-2-8 10:49 | 只看该作者
如果楼主要研究STM32的指令周期,请仔细阅读ARM的相关文件,你这样只靠实验,花费了很多时间和精力,也很难得出正确的结论。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

556

主题

17724

帖子

884

粉丝