Cortext-A7
Cortex-A7 MPcore 处理器支持 1~4 核,性能强,功耗低。
Cortex-A 处理器运行模式
运行模式
Cortex-A7 处理器有 9 种处理模式:
大多数的程序都运行在用户模式,用户模式下是不能访问系统所有资源的,有些资源是受限的,要想访问这些受限的资源就必须进行模式切换。但是用户模式是不能直接进行切换的,用户模式下需要借助异常来完成模式切换,当要切换模式的时候,应用程序可以产生异常,在异常的处理过程中完成处理器模式切换。
当中断或者异常发生以后,处理器就会进入到相应的异常模式种,每一种模式都有一组寄存器供异常处理程序使用,这样的目的是为了保证在进入异常模式以后,用户模式下的寄存器不会被破坏。
寄存器
以下为9种模式所对应的寄存器:
浅色字体的是与 User 模式所共有的寄存器,蓝绿色背景的是各个模式所独有的寄存器。
①、 34 个通用寄存器,包括 R15 程序计数器(PC),这些寄存器都是 32 位的。
②、 8 个状态寄存器,包括 CPSR 和 SPSR。
③、 Hyp 模式下独有一个 ELR_Hyp 寄存器。
16 个 32 位的通用寄存器(R0~ R15)供软件使用,前 15 个(R0~R14)可以用作通用的数据存储。
R0~R7 是未备份寄存器
在不同的模式下,这 8 个寄存器都是同一个物理寄存器,所以数据就会被破坏。
R8~R12是备份寄存器
有两种物理寄存器,其中FIQ 模式下中断处理程序可以使用 R8~R12寄存器,因为 FIQ 模式下的 R8 ~R12 是独立的,因此中断处理程序可以不用执行保存和恢复中断现场的指令,从而加速中断的执行过程。
R13 是堆栈指针SP
有 8 个物理寄存器。
R14 是链接寄存器
有 7 个物理寄存器。
①、每种处理器模式使用 R14(LR)来存放当前子程序的返回地址。
②、当异常发生以后,该异常模式对应的 R14 寄存器被设置成该异常模式将要返回的地址,R14 也可以当作普通寄存器使用。
R15 是程序计数器 PC
ARM 处理器 3 级流水线:取址->译码->执行
PC负责取址,总是指向当前正在执行的指令地址再加上 2 条指令的地址(32位处理器,一条指令=4字节)。
R15 (PC)值 = 当前执行的程序位置 + 8 个字节
当前程序状态寄存器 CPSR 和备份程序状态寄存器 SPSR,SPSR 寄存器就是 CPSR 寄存器的备份。
当特定的异常中断发生时, SPSR 寄存器用来保存当前程序状态寄存器(CPSR)的值,当异常退出以后可以用SPSR 中保存的值来恢复 CPSR(实际操作就是用汇编还可以CPSR入栈进一步保存~)。
模式切换
所有的处理器模式都共用一个 CPSR 物理寄存器,因此 CPSR 可以在任何模式下被访问。所有的处理器模式都共用一个 CPSR 必然会导致冲突,为此,除了 User 和 Sys 这两个模式以外,其他 7 个模式每个都配备了一个专用的物理状态寄存器,叫做 SPSR(备份程序状态寄存器),当特定的异常中断发生时, SPSR 寄存器用来保存当前程序状态寄存器(CPSR)的值,当异常退出以后可以用 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 | T | 描述 |
---|---|---|
0 | 0 | ARM |
0 | 1 | Thumb |
1 | 1 | ThumbEE |
1 | 0 | Jazelle |
GE{3:0}(bit19:16): SIMD 指令有效,大于或等于。
IT{7:2}(bit15:10): 参考 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]: 处理器模式控制位,含义如下表所示:
程序状态寄存器各个位的说明在以后学习中慢慢熟悉。
ARM 汇编基础
Cortex-A 芯片一上电 SP 指针还没初始化, C 环境还没准备
好,所以肯定不能运行 C 代码,必须先用汇编语言设置好 C 环境,比如初始化 DDR、设置 SP指针等等,当汇编把 C 环境设置好了以后才可以运行 C 代码。
GNU 汇编语法
GNU 汇编语法适用于所有的架构,并不是 ARM 独享的, GNU 汇编由一系列的语句组成,每行一条语句,每条语句有三个可选部分,如下:
1.一般结构
label: instruction @ comment
label 即标号,表示地址位置。任何以“:”结尾的标识符都会被识别为一个标号。
instruction 即指令,也就是汇编指令或伪指令。
@符号,表示后面的是注释。
comment 就是注释内容。
代码举例
add:
MOVS R0, #0X12 @设置 R0=0X12
注意! ARM 中的指令、伪指令、伪操作、寄存器名等可以全部使用大写,也可以全部使用小写,但是不能大小写混用。
2.伪操作
.global _start
_start:
ldr r0, =0x12 @r0=0x12
.global 是伪操作,表示_start 是一个全局标号。
常见的伪操作有:
.byte 定义单字节数据,比如.byte 0x12。
.short 定义双字节数据,比如.short 0x1234。
.long 定义一个 4 字节数据,比如.long 0x12345678。
.equ 赋值语句,格式为: .equ 变量名,表达式,比如.equ num,0x12,表示 num=0x12。
.align 数据字节对齐,比如: .align 4 表示 4 字节对齐。
.end 表示源文件结束。
.global 定义一个全局符号,格式为: .global symbol,比如:.global _start。
用户可以使用.section 伪操作来定义一个段,汇编系统预定义了一些段名:
.text 表示代码段。
.data 初始化的数据段。
.bss 未初始化的数据段。
.rodata 只读数据段。
3.函数
函数名:
函数体
返回语句
如:
Undefined_Handler:
ldr r0, =Undefined_Handler
bx r0 @“bx”指令是返回指令,函数返回语句不是必须的
Cortex-A7 常用汇编指令
处理器内部数据传输指令
1、 MOV 指令
用于将数据从一个寄存器拷贝到另外一个寄存器。
MOV R0, R1 @将寄存器 R1 中的数据传递给 R0,即 R0=R1
MOV R0, #0X12 @将立即数 0X12 传递给 R0 寄存器,即 R0=0X12
2、 MRS 指令
用于将特殊寄存器(如 CPSR 和 SPSR)中的数据传递给通用寄存器。
MRS R0, CPSR @将特殊寄存器 CPSR 里面的数据传递给 R0,即 R0=CPSR
3、 MSR 指令
用来将普通寄存器的数据传递给特殊寄存器(如 CPSR 和 SPSR)。
MSR CPSR, R0 @将 R0 中的数据复制到 CPSR 中,即 CPSR=R0
存储器访问指令
存储器有程序存储器(ROM)和数据存储器(RAM)。
寄存器独立于RAM,一般寄存器存在于CPU中用于快速计算。寄存器体积大,速度快;存储器体积小,速度慢。
ARM 不能直接访问存储器( RAM 中的数据),但可借助寄存器间接读取。
1、 LDR 指令
用于从存储器加载数据到寄存器 Rx 中,也可以将一个立即数加载到寄存器 Rx中, LDR 加载立即数的时候要使用“=”,而不是“#”。
LDR R0, =0X0209C004 @将寄存器地址 0X0209C004 加载到 R0 中,即 R0=0X0209C004
LDR R1, [R0] @读取地址 0X0209C004 中的数据到 R1 寄存器中
1.x ADR指令
和LDR类似,他是将标号的地址存到寄存器中。
ADR LR,_start @将标号_start的地址存到LR寄存器中
2、 STR 指令
将数据写入到存储器中
LDR R0, =0X0209C004 @将寄存器地址 0X0209C004 加载到 R0 中,即 R0=0X0209C004
LDR R1, =0X20000002 @R1 保存要写入到寄存器的值,即 R1=0X20000002
STR R1, [R0] @将 R1 中的值写入到 R0 中所保存的地址中
LDR 和 STR 都是按照字进行读取和写入的,指令“LDR/STR”后面加上 B(字节=8位) 或 H(半字=16位)。
LDR 和 STR在操作过程中一般只认寄存器传数据。
压栈和出栈指令
进中断,保存 R0~R15 寄存器的操作就叫做现场保护,
结束中断,恢复 R0 ~R15 寄存器的操作就叫做恢复现场。
栈指针 SP 指向栈顶。
堆栈是向下增长的,之前认识的也没错,以后就按照下图地址增长记忆。
@注意顺序
PUSH {R0~R3, R12} @将 R0~R3 和 R12 入栈,SP-1*5,存数据
STMFD SP!,{R0~R3, R12} @R0~R3,R12 入栈
POP {R0~R3,R12} @在恢复 R0~R3,R12,出数据,SP+1*5
LDMFD SP!, {R0~R3, R12} @再恢复 R0~R3, R12
@出栈命令R1指向的地址到寄存器R10_R11
ldmia r1!, {r10-r11} /* copy from source address [r1] */
@入栈命令寄存器R10_R11到R0指向的地址
stmia r0!, {r10-r11} /* copy to target address [r0] */
跳转指令
有多种跳转操作,比如:
①、直接使用跳转指令 B、 BL、 BX 等。
②、直接向 PC 寄存器里面写入数据。
1、 B 指令
B 指令会将 PC 寄存器的值设置为跳转目标地址。
b main @跳转到 main 函数
2、 BL 指令
BL 指令会将 PC 寄存器的值设置为跳转目标地址,并将返回地址存到LR中(LR寄存器中的内容要提前入栈保护),完事后在回来。
bl system_irqhandler @加载 C 语言中断处理函数到 r2 寄存器中
当通过BL或BLX指令调用子程序时, ** 硬件自动将子程序返回地址保存在LR寄存器中 ** 。在子程序返回时,把LR的值复制到程序计数器PC即可实现子程序返回。如,可以使用MOV PC, LR或者BX LR来完成子程序返回。另外,也可以在在子程序入口处使用下面的指令将LR保存到栈中。
算术运算指令
汇编中也可以进行算术运算
逻辑运算指令
使用汇编语言的时候也可以使用逻辑运算指令,注意:这里每条指令要保证至少两个寄存器。
关于汇编指令,本文暂时记录于此,后续学习中遇到其他指令,在补充。