ARM的三个芯片系列:Cortex-M,Cortex-A,Cortex-R,三个系列芯片有着不同的应用场景:
-
Cortex-A:顶级主控芯片,主要应用于高性能嵌入式系统,它们具有高性能,低功耗和复杂的指令集,可以支持多任务处理和操作系统等复杂应用。
-
Cortex-M:主要针对低成本和低功耗和实时应用,它们有更简单的指令集和更小的尺寸,支持实时响应,低功耗和可靠性高的优点。
-
Cortex-R:主要针对实时应用行业,如航天航空领域或者车载行业,以实时性和高可靠性出名,支持高速计算,纠错和故障保护等应用。
由于我们日常开发中经常接触到的是M和A两个系列的芯片,今天先了解下M系列芯片,以M3为例!
运行模式
首先我们以M3芯片为例,M3芯片采用ARM V7-M架构,具体的芯片架构可以参考下图:
M3芯片包括了所有的16位的thumb指令集和基本的32位thumb-2指令集架构。这两种指令集我们后面有空再了解。
工作模式
M3芯片支持两种工作模式,线程模式和处理模式:
-
在复位时处理器进入线程模式,异常返回时也会进入该模式,特权和用户级代码可以在线程模式下运行。
-
出现异常时的处理器进入处理(handler)模式,该模式中,所有的代码都是特权访问的。
以上是几种模式的切换条件。
M3芯片同样支持两种工作状态:
-
Thumb 状态:该种状态是16位和32位 半字节对齐的thumb 和 thumb-2 指令的正常执行状态。
-
调试模式:可以理解我们说的死机状态,处理器停机调试的时候处于该种模式
那相比于A系列芯片来说,就有多打九种的运行模式,它有除了用户模式以外的8种特权模式:
这些运行模式可以通过软件/中断/异常 来进行来回切换。在用户模式下,程序被禁止访问系统资源,用户可以通过系统调用等方式切换模式.
寄存器组
这里说的是CPU内核的寄存器组。
Cortex M3处理器有以上的32位寄存器组:13个通用寄存器,r0-r12;R13作为堆栈指针SP;R14作为链接寄存器LR;R15作为程序计数器PC;还有一个程序状态寄存器XPSR.
这其中呢通用的r0-r12寄存器在A系列和M系列芯片中是通用的,我们作为通用部分做阐述,先说差异部分!
这里特别需要注意的是:
R13(SP) 作为一个堆栈,在CM3中一共有两个堆栈指针,这也就意味着支持两个堆栈,当引用R13时,你引用到的就是正在使用的那一个,另一个必须要用特殊的指令来访问(MSR,MRS)这两个堆栈指针分别是:
-
主堆栈指针(MSP):它是缺省的堆栈指针,它由OS内核,异常服务例程等特权级的应用程序代码来访问
-
进程堆栈指针(PSP):用以常规的应用程序代码。
我们抓取的死机DUMP里面就能看到MSP和PSP指针的地址。
它通常在进入一个子程序之后,首先需要做的就是把寄存器的值PUSH入栈,栈顶指针SP发生变化,子程序退出后再出栈。
R14(LR) 作为链接寄存器,通常用于调用子程序的时候存储返回地址。例如当我们的程序由mian跳转到func1的时候,PC(R15)指针指向func1,而LR(R14)指针则指向了main.
R15(PC) 称为程序计数器,也就是我们常说的PC指针,可能有的同学并不陌生,在分析DUMP的时候最简单的方法是先看PC指针指向的是哪一个接口。那么这样做的原理是什么样的呢?
其实直接看PC指针并不是一个完全正确的方法,我们首先需要明白的是CM和CA系列芯片普遍采用ARM三级流水线机制,即取指->译码->执行,所以当死机发生时PC指针指向的指令其实并不是真正导致死机的指令,而需要在PC的基础上减去8个字节(32位ARM处理器,一个指令32bit即4个字节)
PC = 当前执行指令地址 + 8字节
下来我们说一下通用寄存器:
R0-R3用作传入函数参数,传出函数返回值。在子程序调用之间,可以将R0-R3用作任何用途。当函数的参数对于4个的时候,会将多出的参数压入栈中,所以这也就是为什么我们常说的函数的入参不建议超过4个。
这里建议大家可以看一下ARM 的ATPCS规则,ATPCS主要定义了函数调用时参数的传递规则和如何从函数返回。
R4-R11 被用来存放函数的局部变量,如果被调用函数使用了这些寄存器,它在返回之前需要恢复这些寄存器的值。
R12 是子程序内部调用的暂存寄存器
而A系列的芯片的寄存器组则有着较大的差异:
可以看到,每一种模式都有着与之对应的寄存器组,这其中有一些模式下拥有自己独立的寄存器,这其中R0-R7可以共享同一组物理寄存器,只有高寄存器组在不同模式下才有自己独特的寄存器。
系统特性
ARM-V7-M的一个重要能力,就是提供了双堆栈的设计:即允许用户应用程序和特权访问程序的堆栈分开,再加上MPU的辅助,可以进一步的阻止用户程序访问内核的堆栈,消除了内核数据被破坏的可能。
MPU将系统内存分为了两个regions,其中一个用作用户级,另外一个用作特权级;分开处理,防止用户级访问造成的CPU内核数据被破坏!两个region分别由主堆栈指针和进程堆栈指针管理地址。
CM3的堆栈是向下生长的满栈,满堆栈即堆栈指针指向的是最后一个可用数据,向下生长可以理解为:CPU在压栈的时候先减地址再存指令。