ARM经历了几十年的发展,其核心架构已经从最初商用的ARMv4发展到了如今的ARMv7,据说ARMv8正处于开发之中,各个版本的内核大同小异,掌握了ARM的思想,其实看着都差不多,就是可配置模块不太一样,或者在指令集,实现方式上做些改变,从软件工程师的角度来看,基本都是一样的,当然如果你是做验证级别的软件工程师,还是要掌握非常细节的部分。
在ARM的官方文档中,对RISC的特性描述如下(当然ARM也都实现了):
1. 较大的寄存器文件(换句话说就是有较多的通用寄存器)
2. load/store架构,就是所有除load/store的数据操作都是在寄存器之间完成,不能够对内存中的数据直接进行。写过单片机程序的人应该有体会,51,96这些都是可以直接对内存中的数进行数学操作的。
3. 简单地寻址模式,所有的load/store地址都是通过寄存器内容和指令算出来的。这点也是区别于CISC的复杂寻址模式,总之就是一条,什么事都尽量交给寄存器。
ARM还对基本的RISC架构进行了如下扩展:
1. 增加了算术移位指令
2. 地址自增和地址自减模式以优化循环操作
3. 多重load/store指令以增大数据处理速度
4. 几乎所有指令都是可以条件执行的,以增加指令处理速度。
关于ARM到底属于什么类型的CPU,一直以来说法不一,有说他们是RISC(至少他们自己这么认为,公司名字就是Advanced RISC Machines的简写)。
有说他们是类RISC的,说他们是类RISC的理由是ARM的指令并不是定长的,而且并没有实现真正的流水线。当然这些区别现在已经变得越来越不重要,甚至各领域之间也在互相渗透,比如MIPS的14K和24K系列也已经引入了16位的指令,支持16/32位指令(ARM/THUMB interworking)混编,而较新的ARM架构中也已经实现了对乱序执行的支持。
可能有人会反驳,怎么可以这么说ARM没有实现流水线呢?ARM7中就已经实现了三级流水线!请注意,这里所说的是真正的流水线,如果对MIPS有所了解或者看过MIPS阵营的经典著作《See MIPS Run》,再结合AMBA文档中对AHB和APB的描述,就能明白真正的流水线的含义。下面我们先对其他架构的流水线结构做一个分析。
提到流水线就要了解一个概念叫做delay slot,也就是我们所说的延迟槽,出现这个概念的原因是因为在RISC中,绝大多数指令的执行时间是可预测的,一般为一个时钟周期。而有些指令譬如load,jmp之类的指令无法在一个周期内完成,这样就造成一个问题,在执行jmp,load时,处于译码阶段的指令就要多等一个周期,等前面的指令执行结束它才能进入执行阶段(早期的ARM就是采用这种方式)。而在真正的流水线架构中,遇到这种情况,可以把jmp,load之后的指令在jmp,load进入执行阶段准备,但是没有真正执行的时候提前执行,这样就节省了之前被浪费的那个周期。处于延迟槽中被提前执行的指令必须是对后续指令无害的,有害还是无害一般是由编译器来决定,判断有害还是无害的标准主要是看延迟槽中的指令结果是否和它前面的指令有关联,当无法插入无害指令时,编译器会向其中插入NOP。千万不要小看这一个周期,因为在真正的软件系统运行过程中,内存读写以及跳转操作随处可见,所占的比例相当大,就这一点点效率的提升就导致了MIPS在整数运算性能上的优势,即使是在如今ARM如此盛行的时代,高端的路由器交换机以及大型图形服务器等依然还是MIPS占据优势(情况可能有变,ARM已经收购了MIPS的64位技术)。
个人曾经遇到过几次关于延迟槽的陷阱,举一个最近的例子来说,在MIPS的bootloader中,一般会在汇编代码前有.set noreorder选项,也就是禁止编译器将代码按照编译器所认为的优化执行顺序重新排序,这主要是因为bootloader相对敏感,并且对那么几行代码重排序也没必要。在bootloader中有一段代码拷贝的地方:
1: addui t1,4
Addui t2,4
Sw t1,[t2]
Jne t2,t4,1b
Mfl t2,$14
麻烦就出在了最后一句,按照顺序思维,应该是执行完了循环再执行它,可是由于他在延迟槽中,每次循环都会去执行它,于是修改了t2的值,造成了地址错误的异常。在ARM中你就不用担心这个陷阱,因为ARM没有延迟槽一说,该等你就得等…。
而支持ARM属于RISC的理由主要在于四点:
1. 使用了精简指令集,Reduced Instruction Set Computer,虽然ARM的指令相对其他种类的RISC架构核心来比较还是算多的,但毕竟处于可接受范围,不像x86架构那样不断添加各种指令,浩瀚无穷。RISC的宗旨就是让硬件尽量简单,更多的处理交给编译器和软件,这样就可以让cpu的核心结构更加简单,比较容易实现低功耗高频率。
2. 实现了一定程度的流水线,学过51的人应该还记得51的工作方式,指令都是一条一条地取指,译码,执行,只有上一条指令执行结束,下一条指令才能进入取指阶段。也就是说cpu在相当长一段时间内只能为一条指令服务,而在RISC架构中,每一时刻基本都会有几条指令处于不同的阶段。
3.ARM核心中有较多的通用寄存器,其数目为31个(不算状态寄存器),要比CISC的通用寄存器数目多很多。但是这31个寄存器并不是同时存在的,有些寄存器是模式专有的,只有处理器处于那种模式才能够对其进行操作,术语称为Banked,在某种模式下可见的通用寄存器的个数是16个。
4.采用了Load-Store的工作方式,所谓的Load-Store方式,也就是说一切的计算操作都只针对寄存器,内存中的数据需要先load到寄存器,在寄存器中进行计算之后再store回内存。而在CISC中,内存数据是可以直接参与计算的,这是双方一个巨大的差别。