笔者落笔此篇的时间是7.1,写上一篇的时间到现在好巧不巧,刚好是一个月。
一个月时间真的发生了一堆怪怪的破事,首先辛苦读者听笔者一顿吐槽好了:
现在写这篇文章的时候笔者正坐在外面的出租屋里面,当然不是因为笔者毕业了、成为社会人了,而是这个暑假学校方面并不想让我们本科生留在学校。经过一段时间的“钉子户生活”,本来形式已经开始向可以留校的好的方向发展了,但是南京突然出现了疫情病例,于是我们就不得不被赶出来了。
学校名字在这里就不提了,隐私。
毕竟是形势所迫,笔者只好表示理解,然后乖乖的搬出学校。但是比赛又不能不打,所以就只好和队友在并不是很远的地方租了个房子,大家一起开始两个月的新生活了。
扯远了,回归正题。
书接上篇:来学学ARM汇编----环境搭建
笔者一向主张开发者的学习应该追根溯源,由于开发使用的Cortex-M4内核是Armv7-M的架构,所以笔者直接找来了Armv7-M架构参考手册用以参考。同时整个学习过程观看了B站某up转载的Sunny老师的视频,干货满满,收获颇丰。
Armv7架构概览
具体了解内核之前先了解一下Armv7架构的概况(其实是翻译一下手册内容):
Armv7被记录为一组架构概要文件。大概定义如下:
Armv7-A 应用程序配置文件,用于支持Arm和Thumb指令集,并且在内存管理模型中需要虚拟地址支持的系统。
Armv7-R 是支持Arm和Thumb指令集的系统的实时配置文件,在内存管理模型中只需要物理地址支持。
Armv7-M 仅支持Thumb指令集的系统的微控制器配置文件,其中实现的总体大小和确定性操作比绝对性能更重要。
虽然在Armv7开发中正式引入了配置文件,但A配置文件和R配置文件在早期版本中已隐含存在,分别与虚拟内存系统体系结构(VMSA)和受保护内存系统体系结构(PMSA)相关。
不是很能看懂,不过没关系,以后有的是机会看懂。
处理器数据类型以及运算
内存中支持的数据类型:
数据类型 | 长度 |
---|---|
Byte(字节) | 8bits |
Halfword(半字) | 16bits |
Word(字) | 32bits |
指令集包含支持寄存器中保存的以下数据类型的指令:
- 32位指针
- 无符号或者有符号32位整数
- 无符号16位或8位整数,以零扩展形式保存
- 有符号16位或8位整数,以符号扩展形式保存
- 无符号或者有符号数,保存在两个寄存器中
加载和存储操作可以在内存中传输字节、半字或字。按照适当的加载指令中的规定,加载字节或半字时零扩展或符号扩展数据。
指令集包括加载和存储操作,这些操作将两个或多个字从内存转移到内存。您可以使用这些指令加载和存储64位整数。
当任何数据类型被描述为无符号时,N位数据值表示0到2^N^-1范围内的非负整数,使用标准二进制格式。
当这些类型中的任何一种被描述为有符号时,N位数据值表示范围为-2^N^-1到+2^N^-1-1的整数,使用二j进制补码格式。
对64位整数的直接指令支持有限,大多数64位操作需要两个或多个指令序列来合成它们。
Arm片上寄存器
Armv7-M中有以下这些与处理器紧密耦合的寄存器,其中包括核心寄存器、专用程序状态寄存器(xPSR)、专用掩码寄存器以及专用控制寄存器等部分。
Arm核心寄存器(R1-R15)
寄存器 | 功能 |
---|---|
R1 | 通用寄存器 |
R2 | 通用寄存器 |
R3 | 通用寄存器 |
R4 | 通用寄存器 |
R5 | 通用寄存器 |
R6 | 通用寄存器 |
R7 | 通用寄存器 |
R8 | 通用寄存器 |
R9 | 通用寄存器 |
R10 | 通用寄存器 |
R11 | 通用寄存器 |
R12 | 通用寄存器 |
R13(SP) | The Stack Pointer(栈指针) |
R14(LR) | The Link Register(链接寄存器) |
R15(PC) | The Program Counter(程序计数器) |
SP寄存器(R13)
SP为栈指针,其中存储当前程序的活跃栈的栈地址。之所以有活跃栈(Active Stack)这一说是由于Armv7-M中其实实现了两个栈:
- 主栈(Main stack)
- 过程栈(Process stack)
当处理器复位时,SP内的值将被预置为主栈的栈顶指针。
LR寄存器(R14)
LR为链接寄存器,用于存储返回链接地址。例如:当发生函数调用时,LR寄存器中将存储子程序的返回地址。
此外,Armv7-M架构手册中还特别强调:
当不需要支持子程序返回时,LR寄存器可用于其他目的。这也是笔者上面将函数调用这种情况仅仅作为一个例子的原因。
LR寄存器的复位值为0xFFFFFFFF,将其复位值直接用于子程序返回会导致处理器发生错误(fault)。
PC寄存器(R15)
PC为程序计数器,存放的是当前正在取址的指令地址。强调取址是由于Arm的三级流水线结构:取址(fetch)、译码(decode)、执行(execute),导致PC指针中其实并不是当前正在执行的指令地址。
当处理器发生复位时,PC指针将会被预置为reset handler函数的首地址。
程序状态寄存器(xPSR)
Armv7-M中共有三种程序状态寄存器:APSR、IPSR以及EPSR(如图):
应用程序状态寄存器(APSR)
标志设置指令修改APSR标志N、Z、C、V和Q,处理器使用这些标志评估其中的条件执行和条件分支指令。重置时各个标志状态未知。
各个标志位如下:
- N标:负数条件标志位,将在每次运算之后被设置为指令结果的[31]位(即有符号数的符号位)。如果结果是有符号整数的二进制补码,那么当结果为负数时N==1,反之结果为正或0,则N==0.
- Z标:零标志位,当运算的结果为0时该位被置1,否则为0。通常用以表示比较的结果相等。
- C标:进位标志位,运算结果发生进位时置1;例如:进行加法运算时出现无符号溢出。
- V标:溢出标志位,运算结果发生溢出时置1;例如:进行加法运算时出现有符号溢出
- Q标:如果SSAT或USAT指令更改结果的有符号或无符号范围的输入值,则设置为1。在实现DSP扩展的处理器中,处理器将该位设置为1,以指示某些乘法溢出。将该位设置为1称为饱和。
IPSR与EPSR暂略
(由于笔者知识储备不足,后续什么时候想起来了再加)
处理器模式
Armv7-M架构的处理器在运行的过程中共有一下几种模式:
- User mode:用户模式,正常的程序执行模式。
- System mode:系统模式,运行特权级的操作系统任务
- Hyp mode:超级监视者,用作虚拟化扩展
- Supervisor mode(SVC):超级管理员模式,管理调用的指令被执行或者reset时。
- Monitor mode:安全扩展模式,用于安全
- Abort mode:退出模式,有一些异常导致的退出
- Undifined mode:未定义模式,未定义的指令执行时
- IRQ mode:一般中断模式
- FIQ mode:快速中断模式
以上模式中,User模式为非特权模式,其余均为特权模式。
以上各种处理器模式中,片上寄存器的共享情况如下:
处理器异常
Armv7-M架构中支持以下异常:
-
Reset 重置异常,永久启用,优先级固定为-3;
Armv7-M中支持两个级别的重置,重置级别决定了当重置完成时那些寄存器字段被预置为其重置值。
- 上电重置会重置处理器、系统空间和调试逻辑;
- 本地重置会重置处理器和系统空间,而某些故障和调试相关的资源则会被保留
-
NMI(Non-Maskable Interrupt)不可屏蔽中断,永久启用,优先级固定为-2;
不可屏蔽中断可由硬件生成,也可由软件将NMI异常设置为挂起状态。
-
HardFault 硬错误,永久启用,优先级固定为-1;
硬错误代表其他所有异常机制都无法处理的通用故障类型。
-
MemManage 内存管理异常,可被软件禁用,具有可配置的优先级,软件禁用后升级为HardFault;
内存管理异常处理由内存保护单元或固定内存保护约束确定的内存保护故障,用于指令和数据内存事务。
-
BusFault 总线错误,可被软件禁用,具有可配置的优先级,软件禁用后升级为HardFault;
总线错误处理指令和数据内存事务中与内存相关的错误(内存管理异常处理的错误除外)。这些故障通常是由系统总线上检测到的错误引起的。该体系结构允许实现根据触发异常的情况报告同步或异步的 Busfault。
-
UsageFault 使用错误,可被软件禁用,具有可配置的优先级,软件禁用后升级为HardFault;
UsageFault故障处理由指令执行引起的与内存无关的故障。例如:
- 未定义指令
- 指令执行状态无效
- 异常返回时出错
- 试图访问禁用或者不可用的协处理器
当配置处理器报告这些错误后,还有以下情况:
- 字节或半字内存访问未对齐地址
- 除以0
-
DebugMonitor 调试监视器,具有可配置的优先级
通常,调试监视器异常是同步异常,并被分类为故障。在禁用“暂停”调试并启用 DebugMonitor 异常时,会发生 DebugMonitor 异常。
-
SVCall 超级管理员调用,永远启用,具有可配置的优先级
处理由SVC指令引起的异常。
-
Interrupts Armv7-M支持两个系统级的中断,最多支持496个外部中断,每个终端都有一个可配置的优先级,两个系统中断如下:
- PendSV 永久启用,由ICSR.PENDSVSET和ICSR.PENDSVCLR两位来控制;
用于软件生成的系统调用。如果应用程序需要底层操作系统提供服务,则使用Supervisor Call(超级管理员调用)。当处理器发生PendSV中断时,执行与PendSV相关的Supervisor Call。
- Systick 永久启用,由ICSR.PENDSTSET和ICSR.PENDSTCLR两位来控制;
该中断由作为 Armv7-M 处理器的一个组成部分的 SysTick 定时器生成。
以上处理器异常的编号如下:
异常编号 | 异常 |
---|---|
1 | Reset |
2 | NMI |
3 | HardFault |
4 | MemManage |
5 | BusFault |
6 | UsageFault |
7-10 | Reserved |
11 | SVCall |
12 | DebugMonitor |
13 | Reserved |
14 | PendSV |
15 | Systick |
16 | External interrupt 0 |
16+N | External interrupt N |
书续下篇:来学学ARM汇编----基本知识(下)