小议ARM Cortex-m0/m4系列的总线差异

下面的一段话来自http://bbs.eeworld.com.cn/thread-1068770-1-1.html,讲述的挺详细的,我认为非常好,因此,都转载下。

同时自己最近使用了cm0+内核的g0芯片,看了芯片资料,内核与外设之间和stm32f0芯片是有区别的,gpio、nvic相连是有各自单独的总线的,因此性能比stm32f0应该是强多了,(stm32f0我看了STM32F098的资料)。也只有一个总线连接,因此想要速度快,肯定是没有cm3或cm4的快了

从一个简单问题说起:STM32的GPIO翻转速度(比如用来模拟时序)最快能多快?
写段代码测试一下:

  1. void test(void)
  2. {
  3.         for(;;)
  4.         {
  5.                    GPIOA->ODR = (1<<5);
  6.                    GPIOA->ODR = 0;
  7.                    GPIOA->ODR = (1<<5);
  8.                    GPIOA->ODR = 0;
  9.                    GPIOA->ODR = (1<<5);
  10.                    GPIOA->ODR = 0;
  11.                    GPIOA->ODR = (1<<5);
  12.                    GPIOA->ODR = 0;
  13.                    GPIOA->ODR = (1<<5);
  14.                    GPIOA->ODR = 0;
  15.                    GPIOA->ODR = (1<<5);
  16.                    GPIOA->ODR = 0;
  17.                    GPIOA->ODR = (1<<5);
  18.                    GPIOA->ODR = 0;
  19.                    GPIOA->ODR = (1<<5);
  20.                    GPIOA->ODR = 0;
  21.                    GPIOA->ODR = (1<<5);
  22.                    GPIOA->ODR = 0;
  23.                    GPIOA->ODR = (1<<5);
  24.                    GPIOA->ODR = 0;
  25.                    GPIOA->ODR = (1<<5);
  26.                    GPIOA->ODR = 0;
  27.                    GPIOA->ODR = (1<<5);
  28.                    GPIOA->ODR = 0;
  29.                    GPIOA->ODR = (1<<5);
  30.                    GPIOA->ODR = 0;
  31.                    GPIOA->ODR = (1<<5);
  32.                    GPIOA->ODR = 0;
  33.                    GPIOA->ODR = (1<<5);
  34.                    GPIOA->ODR = 0;
  35.                    GPIOA->ODR = (1<<5);
  36.                    GPIOA->ODR = 0;
  37.                    GPIOA->ODR = (1<<5);
  38.                    GPIOA->ODR = 0;
  39.                    GPIOA->ODR = (1<<5);
  40.                    GPIOA->ODR = 0;
  41.                    GPIOA->ODR = (1<<5);
  42.                    GPIOA->ODR = 0;
  43.                    GPIOA->ODR = (1<<5);
  44.                    GPIOA->ODR = 0;
  45.         }
  46. }
复制代码


这段代码作用是让 PA5 在高低状态来回翻转,连续20次之后会有一次跳转间隔一下。经过编译器优化处理,变成了一连串的 STR 指令:
......
100001c0:       6159            str     r1, [r3, #20]
100001c2:       615a            str     r2, [r3, #20]
100001c4:       6159            str     r1, [r3, #20]
100001c6:       615a            str     r2, [r3, #20]
100001c8:       6159            str     r1, [r3, #20]
100001ca:       615a            str     r2, [r3, #20]
100001cc:       6159            str     r1, [r3, #20]
......

这样子的,向同一个内存地址(GPIO ODR寄存器的地址)交替写不同的两个值,引起I/O口电平的变化。

如果每条 STR 指令的执行只需要1个机器周期的话(这是最理想的情况),以上程序可以让GPIO输出一半系统时钟频率的方波。实际上是否每条 STR 指令时间是1个机器周期

我曾经在玩8位的AVR的时候,用汇编写过一个ISP下载器的程序,模拟时序也用到了。我到现在也记得清楚,AVR手册上写明了,OUT 指令写I/O空间寄存器是1个机器周期,ST 指令写寄存器或者SRAM要2个机器周期。ARM 的情况呢?我不记得手册上有没有指令周期数的描述,不过可以先测试一下。
为了示波器测量方便,我先把系统时钟频率降到 200kHz. 然后……
 
这个很不错,除了连续20个脉冲之后因为有跳转指令多停顿了一下之外,每段高和低电平都持续了5us,也就是一条STR指令只用了5us, 对应正好一个机器周期

不过先不要太激动,上面这个测试中,代码是在 STM32L452 的 SRAM2 当中执行的。现在我再把代码放在 SRAM1 中执行,结果:
 
是不是很奇怪,不仅变慢了,而且同样的指令执行时间还会变化。我可以猜测,如果在 Cortex-m0 上执行同样的代码,也近乎后面这个效果。

差异从何而来? 

如果你对计算机怎么工作的有所了解的话,能想得到,CPU需要先取得指令,才能够执行指令。那么指令从何处取得呢?单片机上最常用的是 Flash ROM, 还可能是从 SRAM, 甚至从片外挂的 SRAM, SDRAM, NOR Flash 等等。CPU要读内存设备取得指令,就要访问总线。上面的程序里,CPU执行 STR 指令写 GPIO 的寄存器,又是一次总线写操作。好,问题在这里:从总线读内存,与向总线写GPIO设备,这两个操作能否同时进行

然而单片机内部总线也是按照固定的时钟频率来操作的,一个总线 master 在每个总线周期最多只能发出一次请求。下图是 STM32F0x 系列(Cortex-m0)的内部结构
 
CPU 核心和外面的数据通道只有一条 System Bus, 因此它访问 SRAM/Flash 与访问 GPIO 在时间上必须错开。于是就会出现上面第二个画面——总线争用的结果。虽然我的实验并非在 Cortex-m0 上进行,原因是一样的。

那么第一幅画面,那个理想的结果是怎么来的? 看看我实验的这款 Cortex-m4 内部结构图:

Cortex-m4 出来有三条总线,它们是可以并行访问的。分别叫做 I-Code bus, D-Code bus 和 System bus. 这样一来,CPU在从 System bus 写 AHB2 总线上的 GPIO 设备时,CPU还可以同时从 I-Code bus 读 SRAM2 中的程序指令。于是(考虑到流水线操作的结果)就可以达到每个机器周期翻转一次 GPIO 的效果。

上面我的两次实验,代码在 SRAM2 和 SRAM1 中执行效率不同,是因为 SRAM1 是从 System bus 访问的,和访问 GPIO 设备产生了总线争用的问题。倘若把代码放在 Flash 中执行呢?也可能达到和 SRAM2 中执行(从I-Code bus访问)同样的效率,不过有一些条件,因为 Flash 的速度没有 SRAM 快,在CPU频率高的时候必须要插入等待,如果没有缓存(Cache)就会影响速度了。

再说 SRAM1 的问题,上面这个图里面,SRAM1 可是和 Cortex-m4 的三条总线都有连接的呀。我曾经就问过这个问题 http://bbs.eeworld.com.cn/forum.php?mod=viewthread&tid=508085&extra=,现在回答一下:这是按地址空间分的。在 0x20000000 以上的地址,Cortex-m4 (m3也是) 从 System bus 访问,而在 0x20000000 以下的地址,从 I-Code 和 D-Code bus 访问。倘若要提高 SRAM1 中代码的执行效率,需要启用地址重映射:

Cortex-m0 CPU 只有一条总线(因此它属于冯·诺依曼结构,指令和数据统一寻址),就算是执行同样的机器指令程序,也跟有三条总线的 Cortex-m3/m4 (它们属于哈佛结构,指令和数据分开寻址)效率有所差异。为了在 Cortex-m3/m4 上充分发挥这个优势,注意尽量让程序在能从 I-Code bus 访问的存储器设备中执行。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值