1 ARM指令概述
1.1 概述
RISC:精简指令集(Reduced Instruction Set Computer)
在通道中只包含最有用的指令
确保数据通道快速执行每一条指令
使CPU硬件结构设计变得更为简单
RISC CPU包含较少的单元电路,因而面积小、功耗低
1.2 程序执行
1、在正常程序执行过程中,每执行一条ARM指令,程序计数器寄存器(PC)的值加4个字节;每执行一条Thumb指令,程序计数器寄存器(PC)的值加2个字节。整个过程是顺序执行。
2、通过跳转指令,程序可以跳转到特定的地址标号处执行,或者跳转到特定的子程序处执行。其中,B指令用于执行跳转操作;BL指令在执行跳转操作的同时,保存子程序的返回地址;BX指令在执行跳转操作的同时,根据目标地址的最低位可以将程序状态切换到Thumb状态;BLX指令执行3个操作:跳转到目标地址处执行,保存了子程序的返回地址,根据目标地址的最低位可以将程序状态切换到Thumb状态。
3、当异常中断发生时,系统执行完当前指令后,将跳转到相应的异常中断处理程序处执行。在当异常中断处理程序执行完成后,程序返回到发生中断的指令的下一条指令处执行。在进入异常中断处理程序时,要保存被中断的程序的执行现场,在从异常中断处理程序退出时,要恢复被中断的程序的执行现场。
1.3 特点
ARM7具有3级流水线结构(取指、译码、执行),对大多数指令来说每条流水线的处理都是单周期的,不过某些情况下,取指和执行的周期数会延长,导致流水线进入stall状态,指令执行时间超过1个周期。
PC代表程序计数器,流水线使用三个阶段,因此指令分为三个阶段执行:1.取指(从存储器装载一条指令);2.译码(识别将要被执行的指令);3.执行(处理指令并将结果写回寄存器)。
R15(PC)总是指向“正在取指”的指令,而不是指向“正在执行”的指令或正在“译码”的指令。一般来说,将“正在执行“的指令作为参考点,称之为当前第一条指令,因此PC总是指向第三条指令。当ARM状态时,每条指令为4字节长,所以PC始终指向该指令地址加8字节的地址,即:PC值=当前程序执行位置+8;现在PC指向的是正在取指的地址,那么CPU正在译指的指令地址是PC-4(假设在ARM状态下,一个指令占4个字节),CPU正在执行的指令地址是PC-8,也就是说PC所指向的地址和现在所执行的指令地址相差8。当突然发生中断的时候,保存的是PC的地址.这样就知道了,如果返回的时候返回PC,那么中间就有一个指令没有执行,所以用SUB pc lr-irq #4
r0-r12:通用寄存器,其中r8-r12只能被32位指令访问。
r13(SP):堆栈指针;线程模式时可以在线程堆栈和主堆栈之间切换,但处理模式只使用主堆栈。两个堆栈同一时刻只有一个可见,进入、退出异常时自动切换堆栈。
r14(LR):链接寄存器,保存子程序或异常的返回地址(要实现嵌套,必须入栈)。
r15(PC):程序计数器。
xPSR:特殊用途的程序状态寄存器。
2 指令整理
ARM的指令有很多很多,通过功能分类,大致统计为:处理器内传送数据;存储器访问;算术运算;逻辑运算;移位和循环 移位运算;转换(展开和反转顺序)运算;位域处理指令;程序流控制(跳转﹑条件跳转﹑条件执行和函数调用);乘累加(MAC)指令;除法指令;存储器屏障指令;异常相关指令;休眠模式相关指令;其他指令。 当然,Cortex-M4处理器还支持增强DSP指令:SIMD运算和打包指令;快速乘法和 MAC指令;饱和运算;浮点指令(前提是浮点单元存在)。 接下来逐个分类整理介绍16种不同的指令。
使用最高的指令有 B,BL;MOV,MVN;LDR,STR;ADD,SUB,ADC,SBC,MUL;AND,ORR,XOR,TST,BIC;CMP;LDM,STM;nop。
2.1 处理器内传送数据
其中MOV,MVN使用较多。
2.2 存储器访问
其中LDR,STR使用较多。
2.3 算术运算
2.4 逻辑运算
2.5 移位和循环 移位运算
2.6 转换(展开和反转顺序)运算
2.7 位域处理指令
2.8 程序流控制(跳转﹑条件跳转﹑条件执行和函数调用)
2.9 乘累加(MAC)指令
2.10 除法指令
2.11 存储器屏障指令
2.12 异常相关指令
2.13 休眠模式相关指令
2.14 SIMD运算和打包指令
2.15 快速乘法和 MAC指令
2.16 饱和运算
2.17 浮点指令(前提是浮点单元存在)
3 梳理整理
3.1 指令周期数整理
1、大部分算术运算和逻辑运算指令都是单周期的。
2、STR指令需要增加1个总线周期。如果地址位于内部SRAM,则是2个周期;如果地址位于AHB等外设总线上(例如访问外设的寄存器),由于局部总线和外设总线桥接还有额外延时,因此需要再增加一些周期数。
3、LDR指令需要增加2个总线周期。同理,如果地址位于内部SRAM,则是3个周期;如果地址在外设总线上,同样需要再增加一些周期数。
4、对于一次操作多个寄存器的STM、LDM类指令,指令周期数与STR、LDR类似,但每增加一个寄存器需要增加一个总线周期,例如当地址位于内部SRAM、寄存器个数为N时,执行周期数为1+N(STM)和2+N(LDM)。
5、无条件跳转语句和跳转语句成功跳转,需要重新填充流水线,因此至少需要3个周期(假设访问程序存储空间是单周期的)。
6、LDR指令的目标寄存器为R15(PC)时,相当于读总线+跳转,至少需要5个周期(假设访问程序存储空间是单周期的)。
7、乘法指令根据操作数位数的不同,从2-5个周期都有可能。
8、对于所有的带条件的指令,如果条件不满足,指令不被执行,都只需要花1个周期来跳过该指令。
9、实际应用时,还需考虑程序存储器的访问速度(影响流水线取指阶段的周期数)以及Cache的命中情况
3.2 其他整理
1、尽量地少使用跳转指令可以提高程序的执行效率
2、执行分支指令或直接修改pc而发生跳转时,会使ARM内核清空流水线。
3、即使产生中断,一条处于“执行”阶段的指令也将先完成,流水线的其他指令会被放弃,而从向量表的适当入口开始填充流水线。
以上内容参考手册和其他博主整理