前言:
目前针对ARM Cortex-A7裸机开发文档及视频进行了二次升级持续更新中,使其内容更加丰富,讲解更加细致,全文所使用的开发平台均为华清远见FS-MP1A开发板(STM32MP157开发板)
针对对FS-MP1A开发板,除了Cortex-A7裸机开发篇外,还包括其他多系列教程,包括Cortex-M4开发篇、FreeRTOS篇、Linux基础及应用开发篇、Linux系统移植篇、Linux驱动开发篇、硬件设计篇、人工智能机器视觉篇、Qt应用编程篇、Qt综合项目实战篇等。除此之外计划针对Linux系统移植篇、Linux驱动开发篇均会进行文档及视频的二次升级更新敬请关注!
开发板更多资料领取可在评论区下方留言领取~~~
Cortex-A7 架构与工作模式
Cortex-A7 内核工作模式
Cortex-A7 基于 ARMv7-A 架构,共有 9 种工作模式。
除用户模式外的其他 8 种处理器模式称为特权模式(Privileged Modes)。在特权模式下,程序可以访问所有的系统资源,也可以任意地进行处理器模式切换。
处理器模式可以通过软件控制进行切换,也可以通过外部中断或异常处理过程进行切换。
大多数的用户程序运行在用户模式下。当处理器工作在用户模式时,应用程序不能够访问受操作系统保护的一些系统资源,应用程序也不能直接进行处理器模式切换。当需要进行处理器模式切换时,应用程序可以产生异常处理,在异常处理过程中进行处理器模式切换。这种体系结构可以使操作系统控制整个系统资源的使用。
当应用程序发生异常中断时,处理器进入相应的异常模式。在每一种异常模式中都有一组专用寄存器以供相应的异常处理程序使用,这样就可以保证在进入异常模式时用户模式下的寄存器(保存程序运行状态)不被破坏。
寄存器组织
Cortex-A7 处理器寄存器组织概述
Cortex-A7 处理器共有 9 种不同的处理器模式,在每一种处理器模式中都有一组相应的寄存器组,如下图所示,列出了 Cortex-A7 处理器的寄存器组织和模式关系概要。
1、在寄存器组织的图中,每个小方块是一个寄存器,每个寄存器都是 32 位的。
2、寄存器没有地址,访问寄存器通过编号进行访问,r0-r15, cpsr, spsr。
3、banked 类型的寄存器属于私有的寄存器,而非 banked 类型的寄存器属于公有的寄存器。
4、user 模式和 system 模式最多可以访问 17 个寄存器, HYP 模式最多可以访问 19 个寄存器,其他的模式可以访问 18 个寄存器。
5、寄存器组织中只有 43 个寄存器,共计 172 字节,因此在开发中尽量不要定义 register 类型的变量,原因就是寄存器个数有限,寄存器类型的变量不可以进行取地址的操作,原因是寄存器没有地址。
6、当前处理器的模式决定着哪组寄存器可操作,任何模式都可以存取下列寄存器。
1) 相应的 R0~R12、相应的 R13(Stack Pointer,SP,栈指向)和 R14(the Link Register,LR,链路寄存器)。
2) 相应的 R15(PC)、相应的 CPSR。特权模式(除 System 模式外)还可以存取相应的 SPSR。只有HYP 模式可以存取 ELR_hyp。
7、通用寄存器根据其分组与否可分为以下两类:
1) 未分组寄存器(Unbanked Register),包括 R0~R7。对应下图的 Low Registers。
2) 分组寄存器(Banked Register),包括 R8~R12。对应下图的 High Register。
未分组寄存器包括 R0~R7。顾名思义,在所有处理器模式下,对于每一个未分组寄存器来说,指的都是同一个物理寄存器。未分组寄存器没有被系统用于特殊的用途,任何可采用通用寄存器的应用场合都可以使用未分组寄存器。但由于其通用性,在异常中断所引起的处理器模式切换时,其使用的是相同的物理寄存器,所以也就很容易使寄存器中的数据被破坏。需要考虑保护会恢复。
分组寄存器包括 R8~R14,它们每一个访问的物理寄存器取决于当前的处理器模式。
对于分组寄存器 R8~R12 来说,每个寄存器对应两个不同的物理寄存器。一组用于除 FIQ 模式外的所有处理器模式,而另一组则专门用于 FIQ 模式。这样的结构设计有利于加快 FIQ 的处理速度。不同模式下寄存器的使用,要使用寄存器名后缀加以区分。例如,当使用 FIQ 模式下的寄存器时,寄存器 R8 和寄存器 R9 分别记为 R8_fiq、R9_fiq;当使用用户模式下的寄存器时,寄存器 R8 和 R9 分别记为R8_usr、R9_usr 等。在 ARM 体系结构中,R8~R12 没有任何指定的其他的用途,所以当 FIQ 中断到达时,不用保存这些通用寄存器,也就是说,FIQ 处理程序可以不必执行针对 R8~R12 保存和恢复中断现场
的指令,从而可以使中断处理过程非常迅速。所以 FIQ 模式常被用来处理一些时间紧急的任务。
R13(栈指针)寄存器
对于分组寄存器 R13 来说,每个寄存器对应 8 个不同的物理寄存器。其中的一个是 User 模式和 Sys模式公用的,而另外 7 个分别用于 7 种异常模式。文档中描述时,根据需要会指定它们的模式(编码过程中不需要)。名字形式为:R13_。其中,可以是以下几种模式之一:usr、svc、abt、und、irq、fiq、mon、hyp。
R13 寄存器在 ARM 处理器中常用做堆栈指针,称为 SP。当然,这只是一种习惯用法,并没有任何指令强制性的使用 R13 作为堆栈指针,用户完全可以使用其他寄存器作为堆栈指针。而在 Thumb 指令集中,有一些指令强制性地将 R13 作为堆栈指针,如堆栈操作指令。
每一种异常模式拥有自己的 R13。异常处理程序负责初始化自己的 R13,使其指向该异常模式专用的栈地址。在异常处理程序入口处,将用到的其他寄存器的值保存在堆栈中,返回时,重新将这些值加载到寄存器。通过这种保护程序现场的方法,异常不会破坏被其中断的程序现场。
SP 栈指针寄存器中存放的是栈指针指向的栈空间的地址。此栈是用来压栈保存现场的,特点是先进后出。
ARM 的堆栈属于“满减(FD)”栈,SP 栈中的最后一个有效数据,有新数据入栈后,SP 的值减少。比如可以结合之前导入的工程 c_led,在 start.s 文件中,加入一段堆栈测试代码(如果以前没接触过汇编,这个过程也可以等到汇编指令学习以后再做)
示例代码 43-1 堆栈测试
1 /****irq mode stack**/
2 msr cpsr,#0xd2
3 mov sp,r0
4 sub r0,#128*4
5 /*512 byte for irq mode of stack*/
6 // 加入的堆栈测试代码
7 /*******test begin*****/
8 mov r5,#0x11
9 mov r6,#0x23
10 stmfd sp!,{r5,r6}
11 ldmfd sp!,{r7,r8}
12 /*******test end*******/
将 R5、R6、R7、R8、SP 都加入到 Watch 列表中,这样单步调试时可以实时更新。
为了方便观察,可以把数据类型调整为 16 进制显示
设置好断点,对比 R5、R6 入栈之前,和入栈后,SP 指针的位置和内容。下图是入栈前的状态,SP指向的地址:0xc2001f4c,内容是 0。
R5 和 R6 入栈后,SP 指向的地址:0xc2001f44,内容是 0x11。如下图所示。
SP 出栈两个字到 R7、R8 后,SP 的值恢复。
(连接)寄存器
对于分组寄存器 R14 来说,每个寄存器对应 7 个不同的物理寄存器。其中的一个是 User 模式、Sys模式、HYP 模式公用的,而另外 6 个分别用于其它 6 种异常模式。文档中描述时,根据需要会指定它们的模式(编码过程中不需要)。名字形式为:R14_XXX。XXX 可以是以下几种模式之一:usr、svc、abt、und、irp、fiq、mon。
寄存器 R14 又被称为连接寄存器(Link Register,LR),可以被用做通用寄存器使用。在 ARM 体系结构中具有下面两种特殊的作用。每一种处理器模式用自己的 R14 存放当前子程序的返回地址。当通过BL 或 BLX 指令调用子程序时,R14 被设置成该子程序的返回地址。在子程序返回时,把 R14 的值复制到程序计数器(PC)。典型的做法是使用下列两种方法之一。
方法 1:
执行下面任何一条指令。
Mov pc, lr
Bx lr
方法 2:
在子程序入口处使用下面的指令将 PC 保存到堆栈中。
stmfd sp!, {,lr}
在子程序返回时,使用如下相应的配套指令返回。
ldmfd sp!, {,pc}
当异常中断发生时,该异常模式特定的物理寄存器 R14 被设置成该异常模式的返回地址,对于有些模式 R14 的值可能与返回地址有一个常数的偏移量(如数据异常使用 sub pc,lr,#8 返回)。具体的返回方式与上面的子程序返回方式基本相同,但使用的指令稍微有些不同,以保证当异常出现时正在执行的程序的状态被完整保存。
大家可以结合之前导入的工程 c_led,使用反汇编功能,查看子函数调用时的出口部分。更具体的使用,会在 AAPCS 过程调用章节讲解。
R15(程序计数)寄存器
R15 又被称为 PC 程序计数寄存器。PC 寄存器中存储的时当前取指指令(后续流水线章节会解释取指指令的意思)的地址,当完成取指操作之后,PC 中的值会自动加 4 指向下一条指令。
可以仍然使用上面的测试程序,设置好断点,程序运行到设定的断点“mov r5,#0x11”处,此时PC=0xc20000ac,R5=0。
单步执行后 PC 地址加 4,R5=0x11
程序状态寄存器
CPSR 寄存器
当前程序状态寄存器(Current Program Status Register,CPSR)可以在任何处理器模式下被访问,它包含下列内容:
⚫ ALU(Arithmetic Logic Unit,算术逻辑单元)状态标志的备份。
⚫ 当前的处理器模式。
⚫ 中断使能标志。
⚫ 设置处理器的状态。
每一种处理器模式下都有一个专用的物理寄存器做备份程序状态寄存器(Saved Program Status
Register,SPSR)。当特定的异常中断发生时,这个物理寄存器负责存放当前程序状态寄存器的内容。
当异常处理程序返回时,再将其内容恢复到当前程序状态寄存器。
CPSR 寄存器(和保存它的 SPSR 寄存器)中的位分配如图所示。
下面给出各个状态位的定义。
8、标志位
N(Negative)、Z(Zero)、C(Carry)和 V(oVerflow)通称为条件标志位。这些条件标志位会根据程序中的算术指令或逻辑指令的执行结果进行修改,而且这些条件标志位可由大多数指令检测以决定指令是否执行。
在 ARM 4T 架构中,所有的 ARM 指令都可以条件执行,而 Thumb 指令却不能。各条件标志位的具体含义如下。
3) N 标志位
本位设置成当前指令运行结果的 bit[31]的值。当两个由补码表示的有符号整数运算时,N=1 表示运算的结果为负数,N=0 表示结果为正数或零。
4) Z 标志位
Z=1 表示运算的结果为零
Z=0 表示运算的结果不为零。
5) C 标志位
下面分 4 种情况讨论 C 的设置方法:
⚫ 在加法指令中(包括比较指令 CMN),当结果产生了进位,则 C=1,表示无符号数运算发生上
溢出;其他情况下 C=0。
⚫ 在减法指令中(包括比较指令 CMP),当运算中发生错位(即无符号数运算发生下溢出),则
C=0;其他情况下 C=1。
⚫ 对于在操作数中包含移位操作的运算指令(非加/减法指令),C 被设置成被移位寄存器最后移出去的位。
⚫ 对于其他非加/减法运算指令,C 的值通常不受影响
6) V 标志位
下面分两种情况讨论 V 的设置方法。
对于加/减运算指令,当操作数和运算结果都是以二进制的补码表示的带符号的数时,且运算结果超出了有符号运算的范围是溢出。V=1 表示符号位溢出。
对于非加/减法指令,通常不改变标志位 V 的值。
尽管以上 C 和 V 的定义看起来颇为复杂,但使用时在大多数情况下用一个简单的条件测试指令即可,不需要程序员计算出条件码的精确值即可得到需要的结果。
1)Q 标志位
在带 DSP 指令扩展的 ARM v5 及更高版本中,bit[27]被指定用于指示增强的 DAP 指令是否发生了溢出,因此也就被称为 Q 标志位。同样,在 SPSR 中 bit[27]也被称为 Q 标志位,用于在异常中断发生时保存和恢复 CPSR 中的 Q 标志位。在 ARM v5 以前的版本及 ARM v5 的非 E 系列处理器中,Q 标志位没有被定义,属于待扩展的位。
2)J 标志位
在带 Java 指令支持的 ARM v5 及更高版本中支持。T=0;J=1 表示处于 Jazelle 状态
9、控制位
CPSR 的低 8 位(I、F、T 及 M[4:0])统称为控制位。当异常发生时,这些位的值将发生相应的变化。另外,如果在特权模式下,也可以通过软件编程来修改这些位的值。
7) 中断禁止位
I=1,IRQ 被禁止。
F=1,FIQ 被禁止。
8) 状态控制位
T 位是处理器的状态控制位。
T=0,处理器处于 ARM 状态(即正在执行 32 位的 ARM 指令)。
T=1,处理器处于 Thumb 状态(即正在执行 16 位的 Thumb 指令)。
10、模式控制位
M[4:0]作为位模式控制位,这些位的组合确定了处理器处于哪种状态。如表所示列出了其具体含义。
只有下表中列出的组合是有效的,其他组合无效。
状态控制位 M[4:0]
11、IF-THEN 标志位
CPSR 中的 bits[15:10,26:25]称为 if-then 标志位,它用于对 thumb 指令集中 if-then-else 这一类语句块的控制。thumb 语言不是学习的重点(实际应用中,基本都是编译器完成 thumb 的生成),此处不细介绍,大家了解即可。
12、E 控制位
大小端控制位:E=1 设置为大端模式,E=0 为小端模式。
13、A 位
A=1 禁止不精确的数据异常
SPSR 寄存器
spsr:保存程序状态寄存器 ----> Saved Program Statued Register
spsr 寄存器主要用于保存 cpsr 寄存器的。例如下图,在中断情况下的应用。
注意:中断过程中,是由处理器自动将 cpsr 保持到 spsr_irq 的。后续在异常处理章节会有详细讲解。
可以结合之前导入的工程 c_led,在 start.s 文件中,堆栈初始化时,会进入到各个模式下,设置栈地址。可以看到 msr 指令(后续汇编指令章节会介绍)后,cpsr 的 M[4∶0]会变化,然后系统进入到对应的模式。