处理器内核
ARM7TDMI外部接口图:
指令流水线
为了增加处理器指令流的处理速度,ARM7系列采用了三级流水线,允许多个操作同时进行(一个执行的同时,另一个进行译码,另一个进行取址来提高效率),而非顺序操作。注意:pc指向正在被取址的指令,而非正在执行的指令。
最佳流水线
指令周期CPI=6,6个时钟周期执行了6条指令,所有的操作都在寄存器中
LDR流水线
分支流水线
中断流水线
ARM9TDMI
ARM9TDMI使用了哈佛架构增加了指令存储器接口和数据存储器接口,可以实现指令和数据的同时访问,采用的5级流水线,CPI为1.5,提高了最大时钟频率
图中我们可以看到ARM7和ARM9一些区别,ARM7在执行阶段读寄存器,而ARM9在译码阶段读寄存器。
ARM10
下面我们用一张图来总结一下:
ARM汇编指令集
条件执行和标志位
ARM指令都可以通过添加适当的条件码来达到条件执行的目的,举个例子
CMP r3,#0
ADDNE r0,r1,r2
判断R3寄存器中的内容是否等于0,不等于再执行加指令 r0=r1+r2
我们也可以通过添加后缀 S来影响标志位
例如 SUBS
下面我把一些条件码用一张图呈现出来:
ARM数据处理指令机器码格式
31-28位:为指令的条件码
24-21位:指令的操作码
20位:为标志位S
19-16位:Rn第一操作数寄存器的编码
15-12位:Rd目标寄存器的编码
11-0位:操作数(3-0位:第二操作数R寄存器的编码,5-6位:移位方式,11-7位:移动位数)
注意:不能对立即数进行左移右移操作!!
下面我们来展示一些指令的使用加深印象:
1.搬移指令MOV
@ 搬移指令
.text @汇编文本段的开始
mov r0, #5 @r0=5
mov r1,r0 @ r1=r0
mov r2, #1 @ r2=1
mov r3, r2, LSL #3 @r3=r2<<3
mov r4,r3, LSR #2
mrs r6,cpsr @将cpsr状态寄存器的内容搬移到其他寄存器
@mov r1,#0x10
@msr cpsr,r1 @将R1寄存器内容写入cpsr状态寄存器中
.end @汇编文本段的结束
我们在keil中进行编译,可以看到E3A00005是第一条指令的机器码,E为1110是默认的条件码,而24-21位为1101刚好是MOV的操作码,而11-0位为5刚好也是我们的操作数5.
2.逻辑指令 and与,orr或,bic清零
@1.将模式设置为user/system模式
mrs r0, cpsr @将cpsr内容取出放到r0寄存器
and r0, #0xfffffffc @将r0寄存器的内容相与
msr cpsr,r0 @将内容写到cpsr寄存器
@2.orr指令
orr r0,#0x3
mrs r0,cpsr @可以,但是在user模式下可以对cpsr进行读取操作
msr cpsr,r0 @不可以,user模式下不是特权模式,不能对cpsr进行写入操作所以不能变回svc模式
注意,当我们通过更改cpsr状态寄存器来切换模式,将SVC切换到user模式后,不能在user模式下再对cpsr寄存器进行写入将其变回svc状态,但是可以进行读取操作
@3.bic指令将FIQ开启
mrs r0, cpsr
bic r0,#0x40 @将r0中6号位清零
msr cpsr,r0
bic指令可以将立即数中为1的bits位清零,我们可以通过这个操作更改状态寄存器的内容,比如上面我们就将状态寄存器中的6号位清零达到开启FIQ的操作。
3.比较指令CMP,TST
@4.tst指令
mrs r0,cpsr
tst r0,#0x20 @测试r0的第5号位是否为0
@5.cmp指令
cmp r0,#1 @比较r0寄存器中的内容是否和1相等
@6.判断当前状态是否是ARM状态,是则切换到user工作模式
mrs r0,cpsr
tst r0,#0x20
biceq r0,#0x3
msreq cpsr,r0
可以看到在判断之后我们的清零和写入操作都加上了条件码,满足对应的条件才会执行对应的指令
4.跳转指令b,bl
b:跳转到某段标号的代码处
bl:跳转到某个标号处,同时记录下一条指令的地址到LR寄存器
@1.跳转指令b
.text
@编写代码实现1+2+。。。。+10
mov r0,#1
loop: @某段代码的标号
cmp r0,#11
addlt r1,r1,r0 @lt表示条件码
addlt r0,#1
blt loop @跳转到loop
NOP @空指令
.end
可以看到我们代码中加了条件码来达到我们的执行条件就是当r0中寄存器的内容小于11时执行加法指令和跳转指令来达到循环的效果。
@2.跳转指令bl
.text
main:
mov r0,#2
@ bl 跳转到某个标号处,同时记录下一条指令的地址到LR寄存器
bl func1
while:
b while
func1:
mov r6,lr
cmp r0,#2
bleq func2
blne func3
mov pc,r6 @让cpu到lr指定的地址处取指令,也就是回到bl的下一条指令处
func2:
add r0,#3
mov pc,lr
func3:
sub r0,#1
mov pc,lr
.end
bl在执行跳转的时候会将我们下一条指令的地址放到lr中,但是在func1中我们又会进行一次条件跳转可能会导致之前的指令地址被覆盖,所以我们将地址保存到了r6寄存器中,这样func1执行完毕后才能跳转回原来该继续执行的位置。
5.load和store
ldr默认读4个字节,ldrb读一个字节,ldrh读两个字节
下面我们来看看例子:
.text
main:
@ldr伪指令,将buf这个地址不合法的立即数写到r0中
ldr r0,=buf
@ldrb将r0地址上的第一个字节数据读到r1中
ldrb r1,[r0]
@ldrb将r0+4地址上的第一个字节读到r1中
ldrb r1,[r0,#4]
mov r2,#0x9
@strb将r2中的数据前1个字节写入r0+2地址上
@ *(r0+2)=r2
strb r2,[r0,#2]
main_end:
NOP
NOP
.data @汇编文件数据段的开始
buf: @buf相当于缓冲区的首地址(数组名)
@.byte 表示一个字节空间
.byte 0x1,0x2,0x3,0x4,0x5
.end