LV08-01-ARM体系-02-ARM基础知识
本文主要是ARM基础知识——ARM的存储模型、工作模式、寄存器与异常等基础知识相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
| Windows | windows11 |
| Ubuntu | Ubuntu16.04的64位版本 |
| VMware® Workstation 16 Pro | 16.2.3 build-19376536 |
| SecureCRT | Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日 |
| 开发板 | 正点原子 i.MX6ULL Linux阿尔法开发板 |
| uboot | NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03) |
| linux内核 | linux-4.15(NXP官方提供) |
| STM32开发板 | 正点原子战舰V3(STM32F103ZET6) |
| Keil(MDK) | Keil uVision V4.54.0.0 |
点击查看本文参考资料
| 参考方向 | 参考原文 |
| --- | --- |
一、ARM存储模型
1. 数据类型
ARM采用32位架构,基本数据类型有以下三种:
| Byte | 8bits |
| Halfword | 16bits |
| Word | 32bits |
2. 数据存储
Word型数据在内存的起始地址必须是4的整数倍。Halfword型数据在内存的起始地址必须是2的整数倍。即数据本身是多少位在内存存储时就应该多少位对齐。
3. 关于字节序
字节序在前边其实已经接触过了,主要分为两种:
| 大端对齐 | 低地址存放高位,高地址存放低位 |
| 小端对齐 | 低地址存放低位,高地址存放高位 |
【注意】在ARM中,一般使用小端对齐。
例如,以a = 0x12345678为例,
- 大端对齐
| 地址 | 数据 |
| N+0 | 0x12 |
| N+1 | 0x34 |
| N+2 | 0x56 |
| N+3 | 0x78 |
- 小端对齐
| 地址 | 数据 |
| N+0 | 0x78 |
| N+1 | 0x56 |
| N+2 | 0x34 |
| N+3 | 0x12 |
4. 指令存储
处理器处于ARM状态时,所有指令在内存的起始地址必须是4的整数倍。程序计数器PC值由其[31:2]决定,[1:0]位未定义。处理器处于Thumb状态时,所有指令在内存的起始地址必须是2的整数倍。程序计数器PC值由其[31:1]决定,[0]位未定义。
总的来说就是指令本身是多少位在内存存储时就应该多少位对齐。
二、ARM工作模式
以前的 ARM处理器有 7种运行模型: User、 FIQ、 IRQ、 Supervisor(SVC)、 Abort、 Undef和 System,其中 User是非特权模式,其余 6种都是特权模式。但新的 Cortex-A架构加入了TrustZone安全扩展,所以就新加了一种运行模式: Monitor,新的处理器架构还支持虚拟化扩展,因此又加入了另一个运行模式: Hyp,所以 Cortex-A系列的处理器最多可以有9种模式:
| user | 用户模式,大部分程序运行时候的非特权模式,一般在执行上层的应用程序时ARM处于该模式。 |
| FIQ | 快速中断模式,进入FIQ中断异常,当一个高优先级中断产生后ARM将进入这种模式。 |
| IRQ | 一般中断模式,当一个低优先级中断产生后ARM将进入这种模式。 |
| Supervisor(SVC) | 超级管理员,它主要是用来管理调用指令被执行或者reset的时候,当复位或执行软中断指令后ARM将进入这种模式。 |
| Monitor(MON) | 监视模式,为了安全而扩展出的用于执行安全监控代码的模式。 |
| Abort(ABT) | 数据访问终止模式,当产生存取异常时ARM将进入这种模式。 |
| Hyp(HYP) | 是一个超级的监视者的模式,它比超级管理员要稍微低一点,它主要是用来做一些虚拟化的扩展。 |
| Undef(UND) | 它就是未定义的指令执行的时候,比如说一条指令是CPU不识别的,就叫做未定义, 当执行这种未定义的指令时ARM将进入这种模式。 |
| System(SYS) | 系统模式,当系统自己异常的时候,就会发生这样的模式,它有一个特点就是可以进行各个模式的切换,在不同的状态的时候,访问的寄存器就有可能不一样,这个模式用到的寄存器与用户模式是共享的,只是访问级别不一样。 |
上边的所有工作模式中,除了 User也就是用户模式以外,其它8种运行模式都是特权模式。这几个运行模式可以通过软件进行任意切换,也可以通过中断或者异常来进行切换。
大多数的程序都运行在用户模式,用户模式下是不能访问系统所有资源的,有些资源是受限的,要想访问这些受限的资源就必须进行模式切换。但是用户模式是不能直接进行切换的,用户模式下需要借助异常来完成模式切换,当要切换模式的时候,应用程序可以产生异常,在异常的处理过程中完成处理器模式切换。
当中断或者异常发生以后,处理器就会进入到相应的异常模式种,每一种模式都有一组寄存器供异常处理程序使用,这样的目的是为了保证在进入异常 模式以后,用户模式下的寄存器不会被破坏。
三、ARM寄存器组织
1. 寄存器的概念
寄存器是处理器内部的存储器,是没有地址的。它一般用于暂时存放参与运算的数据和运算结果, 包括通用寄存器、专用寄存器、控制寄存器。
2. ARM寄存器
ARM架构提供了 16个 32位的通用寄存器 (R0~R15)供软件使用,前 15个 (R0~R14)可以用作通用的数据存储, R15是程序计数器 PC(Program Counter),用于存储当前取址指令的地址。另外 ARM还提供了一个当前程序状态寄存器 CPSR和一个备份程序状态寄存器 SPSR, SPSR寄存器就是 CPSR寄存器的备份。
3. 不同模式下的寄存器组
在上边介绍的寄存器中,有些是所有模式所共用的同一个物理寄存器,有一些是各模式自己所独立拥有的,各个模式所拥有的寄存器如下表:
| User | SYS | FIQ | IRQ | ABT | SVC | UND | MON | HYP |
| R0 | R0 | R0 | R0 | R0 | R0 | R0 | R0 | R0 |
| R1 | R1 | R1 | R1 | R1 | R1 | R1 | R1 | R1 |
| R2 | R2 | R2 | R2 | R2 | R2 | R2 | R2 | R2 |
| R3 | R3 | R3 | R3 | R3 | R3 | R3 | R3 | R3 |
| R4 | R4 | R4 | R4 | R4 | R4 | R4 | R4 | R4 |
| R5 | R5 | R5 | R5 | R5 | R5 | R5 | R5 | R5 |
| R6 | R6 | R6 | R6 | R6 | R6 | R6 | R6 | R6 |
| R7 | R7 | R7 | R7 | R7 | R7 | R7 | R7 | R7 |
| R8 | R8 | R8_fiq | R8 | R8 | R8 | R8 | R8 | R8 |
| R9 | R9 | R9_fiq | R9 | R9 | R9 | R9 | R9 | R9 |
| R10 | R10 | R10_fiq | R10 | R10 | R10 | R10 | R10 | R10 |
| R11 | R11 | R11_fiq | R11 | R11 | R11 | R11 | R11 | R11 |
| R12 | R12 | R12_fiq | R12 | R12 | R12 | R12 | R12 | R12 |
| R13(SP) | R13(SP) | SP_fiq | SP_irq | SP_abt | SP_svc | SP_und | SP_mon | SP_hyp |
| R14(LR) | R14(LR) | LR_fiq | LR_irq | LR_abt | LR_svc | LR_und | LR_mon | R14(LR) |
| R15(PC) | R15(PC) | R15(PC) | R15(PC) | R15(PC) | R15(PC) | R15(PC) | R15(PC) | R15(PC) |
| CPSR | CPSR | CPSR | CPSR | CPSR | CPSR | CPSR | CPSR | CPSR |
| - | - | SPSR_fiq | SPSR_irq | SPSR_abt | SPSR_svc | SPSR_und | SPSR_mon | SPSR_hyp |
| - | - | - | - | - | - | - | - | ELR_hyp |
4. 专用寄存器说明
4.1 R13(SP,Stack Pointer)
栈指针,用于存储当前模式下的栈顶地址。
4.2 R14(LR,Link Register)
链接寄存器,一般有以下两种用途:
(1)执行跳转指令(BL/BLX)时,LR会自动保存跳转指令下一条指令的地址,当程序需要返回时将LR的值复制到PC即可实现程序返回。
(2)产生异常时,对应异常模式下的LR会自动保存被异常打断的指令的下一条指令的地址,异常处理结束后将LR的值复制到PC可实现程序返回。
当执行跳转指令或产生异常时,LR寄存器中不会凭空产生一个返回地址其原理是当执行跳转指令或产生异常时,处理器内部会将PC寄存器中的值拷贝到LR寄存器中,然后再将LR寄存器中的值自减4。
点击查看 LR 在 BL 和 IRQ 中断的不同
- BL
当执行BL指令时,指令执行过程中处理器内部就会将PC寄存器的值拷贝到LR寄存器,然后再将LR寄存器中的值自减4, 所以LR寄存器中保存的就是BL指令下一条指令的地址。
- IRQ中断
当正在执行一条指令时产生了一个IRQ中断,执行这条指令过程中处理器不会保存返回地址,而是执行完成后才会保存PC寄存器中的值到LR寄存器,但发生IRQ时正在执行的指令在执行完成后PC的值又会自动增4,所以对于IRQ来说LR中保存的是被中断打断的指令的下下条指令的地址。所以我们在写中断处理的时候要注意修正这个问题,否则就会少一条指令。
另外R14也可以作为普通寄存器使用。
4.3 R15(PC,Program Counter)
程序计数器,用于存储当前取址指令的地址。对于三级流水线的ARM处理器来说,R15 保存着当前执行的指令地址值加8 个字节,这是因为ARM的流水线机制导致的。ARM 处理器的3级流水线为:取指→译码→执行,这三级流水线循环执行,比如当前正在执行第一条指令的同时也对第二条指令进行译码,第三条指令也同时被取出存放在R15(PC)中。
我们以当前正在执行的指令作为参考点的话,也就是以第一条指令为参考点,此时R15(PC)中存放的就是第三条指令,换句话说就是R15(PC)总是指向当前正在执行的指令地址再加上2 条指令的地址。对于32 位的 ARM 处理器,每条指令是 4 个字节,所以有:
1 | R15 (PC)值 = 当前执行的程序位置 + 8 个字节 |
5. CPSR和SPSR
所有的处理器模式都共用一个CPSR 物理寄存器,因此CPSR 可以在任何模式下被访问。CPSR 是当前程序状态寄存器,该寄存器包含了条件标志位、中断禁止位、当前处理器模式标志等一些状态位以及一些控制位。
所有的处理器模式都共用一个CPSR必然会导致冲突,为此,除了User 和Sys这两个模式以外,其他 7 个模式每个都配备了一个专用的物理状态寄存器,叫做SPSR(备份程序状态寄存器),当特定的异常中断发生时,SPSR 寄存器用来保存当前程序状态寄存器(CPSR)的值,当异常退出以后可以用SPSR 中保存的值来恢复CPSR。因为User 和Sys这两个模式不是异常模式,所以并没有配备SPSR,因此不能在User 和Sys 模式下访问SPSR,会导致不可预知的结果。由于SPSR 是CPSR 的备份,因此SPSR 和CPSR 的寄存器结构相同。结构如下图所示:
N(bit31):当两个补码表示的有符号整数运算的时候,N=1表示运算对的结果为负数,N=0表示结果为正数。Z(bit30):Z=1表示运算结果为零,Z=0表示运算结果不为零,对于CMP指令,Z=1表示进行比较的两个数大小相等。C(bit29):在加法指令中,当结果产生了进位,则C=1,表示无符号数运算发生上溢,其它情况下C=0。在减法指令中,当运算中发生借位,则C=0,表示无符号数运算发生下溢,其它情况下C=1。对于包含移位操作的非加减法运算指令,C中包含最后一次溢出的位的数值,对于其它非加减运算指令,C位的值通常不受影响。V(bit28):对于加减法运算指令,当操作数和运算结果表示为二进制的补码表示的带符号数时,V=1表示符号位溢出,通常其他位不影响V位。Q(bit27):仅ARM v5TE_J架构支持,表示饱和状态,Q=1表示累积饱和,Q=0表示累积不饱和。IT[1:0](bit26:25):和IT[7:2](bit15:bit10)一起组成IT[7:0],作为IF-THEN指令执行状态。J(bit24):仅ARM_v5TE-J架构支持,J=1表示处于Jazelle状态,此位通常和T(bit5)位一起表示当前所使用的指令集。
| J(bit24) | T(bit5) | 指令类型 |
| 0 | 0 | ARM |
| 0 | 1 | Thumb |
| 1 | 1 | ThumbEE |
| 1 | 0 | Jazelle |
GE[3:0](bit19:SIMD指令有效,大于或等于。IT[7:2](bit15):参考IT[1:0]。E(bit9):大小端控制位,E=1表示大端模式,E=0表示小端模式。A(bit8): 禁止异步中断位,A=1表示禁止异步中断。I(bit7):I=1禁止IRQ,I=0使能IRQ。F(bit6):F=1禁止FIQ,F=0使能FIQ。T(bit5): 控制指令执行状态,表明本指令是ARM指令还是Thumb指令,通常和J(bit24)一起使用表明指令类型,参考J(bit24)位。M[4:0]处理器模式控制位, 取值与模式对应情况如下:
| M[4:0] | 处理器模式 |
| 10000 | User模式 |
| 10001 | FIQ模式 |
| 10010 | IRQ模式 |
| 10011 | Supervisor(SVC)模式 |
| 10110 | Monitor(MON)模式 |
| 10111 | Abort(ABT)模式 |
| 11010 | Hyp(HYP)模式 |
| 11011 | Undef(UND)模式 |
| 11111 | System(SYS)模式 |
四、ARM异常处理
1. 什么是异常?
处理器在正常执行程序的过程中可能会遇到一些不正常的事件发生,这时处理器就要将当前的程序暂停下来,转而去处理这个异常的事件,当异常事件处理完成之后再返回到被异常打断的点继续执行程序。
2. ARM异常源
导致异常产生的事件称为异常源。ARM中的异常源如下:
| FIQ | 快速中断请求引脚有效 |
| IRQ | 外部中断请求引脚有效 |
| Reset | 复位电平有效 |
| Software Interrupt | 执行swi指令 |
| Data Abort | 数据终止 |
| Prefetch Abort | 指令预取终止 |
| Undefined Instruction | 遇到不能处理的指令 |
3. ARM异常模式
在ARM的基本工作模式中有5个属于异常模式,即ARM遇到异常后会切换成对应的异常模式。异常源对应的异常模式如下:
| 异常源 | FIQ | IRQ | Reset/SWI | Data Abort/Prefetch Abort | Undef Instruction |
| 异常模式 | FIQ | IRQ | SVC | Abort | Undef |
4. 异常响应
产生异常时,ARM进入异常模式,开始处理异常处理程序,处理完异常后回到原来的用户处理程序:
ARM产生异常后的动作(自动完成)- 1.拷贝
CPSR中的内容到对应异常模式下的SPSR_<mode>。 - 2.修改
CPSR的值。
(1) 修改中断禁止位禁止相应的中断。
(2) 修改模式位进入相应的异常模式。
(3) 修改状态位进入ARM状态。 - 3.保存返回地址到对应异常模式下的
LR_<mode>。 - 4.设置
PC为相应的异常向量(异常向量表对应的地址)。
- 1.拷贝
ARM异常返回的动作(自己编写)- 5.将
SPSR_<mode>的值复制给CPSR使处理器恢复之前的状态。 - 6.将
LR_<mode>的值复制给PC使程序跳转回被打断的地址继续执行。
- 5.将
【注意】整个过程CPSR保存的永远是当前程序运行状态,SPSR只是异常时对原来的CPSR进行备份。
5. 异常向量表
当发生异常的时候,程序会跳转到异常向量表,异常向量表的本质是内存中的一段代码。异常向量表中为每个异常源分配了四个字节的存储空间,遇到异常后处理器自动将PC修改为对应的地址,因为异常向量表空间有限,所以一般我们不会再这里写异常处理程序,而是在对应的位置写一条跳转指令使其跳转到指定的异常处理程序的入口。
【注意】ARM的异常向量表的基地址默认在0x00地址,但可以通过配置协处理器来修改其地址。
6. 异常优先级
多个异常产生时,同一时刻不能处理好几个异常吧,异常处理的优先级如下:
7. FIQ和IRQ
一般来说,FIQ的响应速度比IRQ快,原因如下:
- (1)
FIQ在异常向量表位于最末可直接把异常处理写在异常向量表之后,省去跳转步骤; - (2)
FIQ模式有5个私有寄存器(R8-R12) 执行中断处理程序前无需压栈保存寄存器,可直接处理中断; - (3)
FIQ的优先级高于IRQ:
两个中断同时发生时先响应FIQ;
FIQ可以打断RIQ,但RIQ不能打断FIQ。
8. 异常处理实例
这里以IRQ异常为例,具体分析一下异常的处理过程,具体过程如下图所示: