文章目录
ARM体系结构与编程
当前ARM核的版本划分,
- ARMV4:这其中包含的小版本有:
- ARM720T
- ARM920T
- ARMV5:
- Xscale
- ARM10
- ARMV6:
- ARM11
- ARMV7:
- ARM-CORTEX-A8
- ARM-CORTEX-A9
- ARM-CORTEX-A15
- ARM-CORTEX-M3/M0 STM32(M3)主打控制处理
- ARMV8:(正式支持64位)
- ARM-CORTEX-A53
- ARM-CORTEX-A57
ARM7(ARMV4)和ARM9之后的指令操作流程
ARM7使用的是三级指令流水线,但是ARM9和ARM9之后的版本都使用五级指令流水线。其中需要说明:
- 指令:给CPU核下发的命令,CPU核根据命令完成一个功能。(例如:给CPU核发一个add指令,就是告诉CPU核做数据的加法运算)
- 指令在哪里呢:指令就是存在于二进制可执行文件中,CPU核要想执行指令,首先将指令对应的二进制文件加载到内存中,指令最终存在于内存中,内存的访问以地址的执行访问,所以每一条指令也有对应的内存存储地址,当CPU要想运行一条指令,首先从内存中获取到指令。
- 注意: CPU核工作在ARM状态下:一条指令的宽度为32位(4字节);CPU核工作在THUMB状态下:一条指令的宽度为16位(2字节)。
ARM7(ARMV4)三级指令流水线
三级流水线
- 取指:CPU核中的取指器(硬件)从内存中读取要运行的指令。
- 解码:CPU核取完指令后,该指令还不能直接运行(CPU核不识别),需要使用解码器进行解码成机器码。
- 执行:解码完成后,CPU核最终执行指令。
-
总结: 取指(F)->解码(D)->执行(E)
PC
- PC是个寄存器,位宽为32位。它是ARM核(CPU核)中的寄存器,用来保存取指器要取的那条指令对应的内存存储地址。
例如:
#内存存储地址 指令
0x48000000 add
0x48000004 sub
0x48000008 and
....
当上述add指令呗执行的时候,取指器取得and指令,所以此时PC=0x48000008。
ARM7三级流水线特例——ldr指令
ldr指令功能:CPU核利用此指令能够从外设中加载数据到CPU核中。
ldr指令流水线分为五级:
取指(F)->解码(D)->执行(E)->访存(M)->写回(W)
说明:
- 访存:CPU核寻找外设地址。
- 写回:CPU核一旦找到了外设地址,将外设的数据加载到CPU核中。
- 注意:ARM7的ldr指令在M和W的时候,跟在其后的两条指令的处理需要延后两个指令周期。
ARM7三级流水线特例——bl指令
bl指令的功能:CPU核执行bl指令,能够跳转到bl指令指定的内存位置去运行(取指->解码->执行)。
bl指令处理流水线需要五级:
取指(F)->解码(D)->执行(E)->保存返回地址(L)->正式跳转(A)
说明:
- 保存返回地址(L):CPU核跳转到bl指令指定的地址运行之前先把返回地址(bl治疗的下一条指令)进行保存处理。
- 正式跳转(A):CPU核跑到bl执行指定的地址去运行。
ARM7三级流水线特例——中断
中断定义:中断是计算机非常重要的机制
问:计算机中为什么有中断呢?
- 中断产生的根本原因就是CPU的数据除了速度远远快于外设的数据处理速度。举个例子:以CPU核读取UART接收缓冲区的数据为例,当CPU核发现UART接收缓冲区没有数据,通常的操作都是轮询方式(死等),判断接收缓冲区有没有数据到来,但是这种放松会大大消耗CPU的利用率,此时此刻就需要用到中断方式操作UART接收缓冲区。当接收缓冲区有数据,UART控制器会给CPU发送一个中断信号告诉CPU有数据到来,CPU就会停止手上的工作转而去处理UART接收数据,之后在回到之前的流程中继续处理刚刚的事情。这样大大提高了CPU的利用率。
CPU核接收到中断信号后,开始处理中断,中断处理流水线需要四级:
切记: 当CPU核正在执行某个处理时,CPU核受到中断信号,CPU核处理中断一定是在当前处理执行完之后再去处理。
解码中断(DI)->执行中断(EI)->保存返回地址(L)->正式跳转(A)
说明:
- 保存返回地址:中断产生之前,CPU在执行别的指令,当中断到来,CPU核转而执行中断处理函数,处理完还需要再回来继续处理之前的指令,所以需要保存返回地址。
ARM9(ARMV4)以后的ARM核的五级指令流水线
ARM9以后的所有ARM核指令流水线一律采用五级流水线,也就是将访存(M)和写回(W)给每一条指令都添加上。
- F->D->E->M->W
ARM编程模型
ARM核的七种工作模式
- SVC管理模式: 当系统复位或者软件执行swi指令,CPU核切换到此模式下。
- FIQ快速中断模式: 外设给CPU核发送一个FIQ快速中断信号,CPU核会切换到此模式下。
- IRQ中断模式: 外设给CPU核发送一个IRQ普通中断信号,CPU核切换到此模式。
- Abort中止模式: 当CPU核取指失败或者访存失败,CPU核进入此模式。
- undef未定义指令模式: 当CPU核执行一个不认识的指令(lisi),CPU核进入此模式。
- system系统模式:
- user用户模式: system和user一样,CPU核正常运行情况下,CPU就会处于这两个模式其中之一。Linux系统下,当CPU处理一个进程时就工作在user模式下。
ARM核的两种工作状态
ARM状态:
- ARM指令:位宽 32 位。
Thumb状态: - thumb指令:位宽 16 位。
ARM核的37个寄存器
- 寄存器:暂存数据的硬件,类似内存
- 寄存器分类:特殊功能寄存器和ARM寄存器。
- 特殊功能寄存器:各种硬件控制器内核的寄存器,也就是之前说的CPU通过访问这样一类寄存器都是通过地址指针形式访问。
- ARM寄存器:仅仅存在于ARM核内部,CPU核访问内部的ARM寄存器通过名字(不论大小写),总共37个ARM寄存器。
ARM核37个寄存器:
- 31个通用寄存器:r0, r1, r2, … … r15,每一种工作模式下又有自己独立的寄存器。
- r15又称gc:永远只保存取指器要取的那条指令的内存存储地址。
- r14又称lr:永远只保存返回地址。
- r13又称sp:永远只保存栈指针。
6个状态寄存器:
- 1个cpsr和5个spsr
- cpsr:又称当前程序状态寄存器,也就是保存当前程序运行状态。
- spsr:又称备份程序状态寄存器,本质目的就是为了保存cpsr的值。
详解CPSR程序状态寄存器:
- BIT[4:0]:模式位mode
- 10000 ,表示当前CPU核处于用户模式。
- 10011 ,表示当前CPU核处于SVC管理模式。
- BIT[5]:状态位T
- 0 - ARM核处于ARM状态
- 1 - ARM核处于thumb状态
- BIT[6]:FIQ配置位F
- 0 - 使能FIQ中断功能
- 1 - 禁止FIQ中断功能
- BIT[7]:IRQ配置位T
- 0 - 使能IRQ中断功能
- 1 - 禁止IRQ中断功能
- BIT[28]:溢出标志位V
- 0 - 表示程序运行结果没有发送溢出。
- 1 - 表示程序运算结果发送溢出
- BIT[29]:进位或者借位表示C
- 0 - 表示程序运算没有发送进位或者借位。
- 1 - 表示程序运算时发送了进位或者借位。
- BIT[30]:零位Z
- 0 - 表示程序运算结果为非0
- 1 - 表示程序运算结果为0
- BIT[31]:负或者小于位N
- 0 - 表示程序运算结果不为负数或者不小于
- 1 - 表示程序运算结果为负数或者小于
- BIT[31:28]简称NZCV,只要程序运行,CPSR的值会随时随刻发生改变。
注意:
- 现在有一个进程获取到CPU资源,此进程就可以投入运行,此时对应的CPU核的工作模式为User用户模式,此时由于CPU核执行进程对应的代码cpsr时刻进行改变,突然UART控制器给CPU核发送一个IRQ中断电信号,此时CPU核就会由 User用户模式切换到IRQ普通中断模式,紧接着CPU核开始去处理IRQ中断,本质就是CPU核去执行这个IRQ中断对应的一个函数而已,此函数也是一个程序,如果此程序运行,将来势必也会影响cpsr,但是将来中断处理完毕CPU核还要返回到原先被打断的进程继续运行,为了让原先的进程能够继续正确运行,所以CPU核在处理中断之前,先把当前进程的cpsr的值备份到IRQ模式下的spsr,将来中断返回 只需从IRQ模式下的spsr恢复原先进程的cpsr。
ARM支持的数据类型
Byte:1字节
HalfWord:2字节
Word:4字节
DoubleWord:8字节
ARM支持大端和小端模式,默认小端模式
X86是小端,PowerPC是大端。
ARM汇编语法格式:
参考代码:
.text @代码段的起始
.code 32 @使用ARM指令
.global start @声明一个全局标签start
start: @全局标签的内容如下
mov r0, #10 @r0=10
ldr r1, =3 @r1=3
add r0, r0, r1@r0=r0+r1
b . @while(1);
.end @代码段的结束
切记:影响CPSR的NZCV位的两种情形:
- 1.指令后面加s,运算结果影响CPSR的NZCV
add r0, r0, r1 #@r0=1,r1=-1
#@r0保存运算结果,不影响CPSR的NZCV
adds r0, r0, r1 #@r0=1,r1=-1 @CPSR的Z=1
- 2.cmp比较指令后面不加s,运算结果同样影响cpsr
cmp比较指令本质做减法运算
cmp r0, r1 #@本质:运算结果=r0-r1
ARM异常
ARM核有7种异常:
异常 | 工作模式 | CPU核运行地址 | 场景 |
---|---|---|---|
复位异常 | SVC管理模式 | 0x00 | 系统复位 |
undef未定义指令异常 | undef未定义指令模式 | 0x04 | CPU核执行一个不认识的指令 |
软中断异常 | SVC管理模式 | 0x08 | CPU核执行swi指令 |
预取指令异常 | abort中止模式 | 0x0C | CPU核取指失败 |
数据处理异常 | abort中止模式 | 0x10 | CPU核访存失败 |
IRQ中断异常 | IRQ中断模式 | 0x18 | 外设给CPU发送IRQ中断电信号 |
FIQ中断异常 | FIQ快速中断模式 | 0x1C | 外设给CPU核发送FIQ快速中断电信号异常 |
- 注意:一旦异常发生,CPU核会毫无条件的必须强制跑到对应的入口地址去执行。
ARM一旦触发异常会如何处理
以UART控制器给CPU核发送IRQ中断电信号为例,
理清IRQ异常的处理流程:
- 1.当UART控制器准备好数据以后,首先会给中断控制器发送一个中断电信号。
- 2.中断控制器接收到这个中断电信号以后经过一番的判断之后,中断控制器最终以IRQ的形式给CPU核发送一个IRQ中断电信号。
- 3.CPU核一旦接收到IRQ中断电信号,就会触发一个IRQ异常,CPU核立马要处理这个IRQ异常。
CPU核硬件上自动完成以下工作:
-
1.把当前程序对应的cpsr的值保存到IRQ中断模式下的spsr_irq
-
2.设置cpsr,将cpsr的:
bit[4:0]=IRQ工作模式对应的值,让CPU核
切换到IRQ中断模式
bit[5]=0,让ARM核切换到ARM状态下
bit[7:6]=11,禁止ARM核响应IRQ和FIQ中断 -
3.保存返回地址:lr=pc-4
- 问:为什么是lr=pc-4呢?
- 答:例如:
内存地址 指令
0x8000 add
0x8004 sub
0x8008 orr
当add指令执行的时候:pc=0x8008
当add指令执行的时候,触发IRQ异常
将来IRQ异常处理完毕,CPU核返回到0x8004
去运行,所以lr=pc-4=0x8008-4=0x8004
-
4.设置pc=0x18
- 问:为什么0x18呢?
- 答:因为IRQ异常处理的入口地址为0x18
- 问:pc=0x18代表什么含义呢?
- 答:本质就是让CPU核跑到0x18地址去运行(F->D->E->M->W)。
- 问:0x18地址放什么东西?
- 答:0x18势必放自己编写的软件代码
切记:只要给pc赋值(地址),那么CPU核就会跑到对应的地址去运行。
- 结论:一旦ARM核做pc=0x18,让CPU核跑到0x18地址去运行,至此正式开启了软件进一步处理IRQ异常的流程。
软件处理IRQ异常的流程:
- 软件如何处理IRQ异常完全有程序员自行决定,软件处理IRQ异常结束以后,最后让CPU核返回到原先被打断的地方(某个程序)继续执行,软件只需完成以下两个动作即可
- 实现返回:
- cpsr=spsr_irq:将IRQ模式下的spsr的值归还给cpsr,spsr_irq保存原先被打断的程序运行的状态。
- pc=lr:lr保存的返回地址,让CPU核跑到原先被打断的地方继续执行
- 至此:IRQ异常处理完毕!
ARM核跳转的方式
ARM核跳转的方式有三种:
- ARM核发生异常,一旦发生异常,ARM核跑到对应的异常入口地址去。
- ARM核执行b或者bl跳转指令。
- 直接向pc赋值(此方法最直接)
ARM指令——分支跳转指令
ARM指令集之分支跳转指令:b/bl/bx
b为不带返回的跳转指令
- 跳转范围相对PC,±32MB
b loop 无条件跳转
beq loop 有条件跳转
bl为待返回的跳转指令
- 跳转范围相对PC,±32MB
切记:当CPU核执行bl指令时,CPU核硬件上自动将
下一条指令的地址保存在lr中
所以将来软件返回的指令为:mov pc, lr
例如:
内存地址 指令
0x8000 bl strcmp @当bl执行时,pc=0x8008,lr=0x8004
0x8004 add r0, r0, r1
0x8008 sub r0, r0, r1
strcmp:
一大堆指令
mov pc, lr @pc=lr=0x8004,让CPU核跑到0x8004去运行
bx带状态切换的指令(了解)
- bx跳转的地址的bit[0]=0:切换到ARM状态
bx跳转的地址的bit[0]=1:切换到Thumb状态
例如:
ldr r0, =0x48000000
bx r0 @让CPU核切换到ARM状态并且跳转到0x48000000去运行
ldr r0, =0x48000001
bx r0 @让CPU核切换到Thumb状态并且跳转到0x48000001去运行