- Core
- Banked
- System
- Internal
- FPU
ARM Cortex-M4为32位处理器内核,该处理器包含以下32位寄存器:
- 13个通用寄存器(R0~R12)
- 堆栈指针 SP,别名为“Banked”寄存器
- 主堆栈指针 MSP (SP_main)
- 进程堆栈指针 PSP (SP_process)
- 连接寄存器 LR(R14)
- 程序计数寄存器 PC(R15)
- 特殊功能程序状态寄存器 xPSR
如上图,R0 ~ R12为32位通用寄存器,通用寄存器又分为高组和低组,至于通用寄存器的作用(可以回去看看51内核的R0 ~ R7)可能有人会说我们用C编程的时候不会考虑它们,只有汇编才会用到,但是实际上无论是在启动代码部分还是操作系统移植的时候都会接触到它们,甚至到以后考虑程序优化的时候也会用到的,所以重要性不言而喻,这也是我为什么最开始就介绍它的理由之一。下面就分别介绍下这几个寄存器(参考了CM3权威指南部分资料,因为在寄存器组方面CM4和CM3是一样的):
- 通用寄存器R0~R7:为低组寄存器,所有指令都可以访问,大小为32位,复位后初始值不定;
- 通用寄存器R8~R12:为高组寄存器,只有部分的16位Thumb指令可以访问,32位Thumb-2指令不受限制,大小为32位,复位后初始值仍然不定;
- R13为堆栈指针(即SP,这个应该都知道了),CM4内核和CM3内核是一样的,有两个堆栈指针(注意都是共用R13,只不过不能同时访问罢了,引用R13即引用当前使用的指针(MSP or PSP))。其中MSP(主堆栈指针)即默认的堆栈指针,一般由OS内核、异常服务例程以及所有需要特权访问的应用程序代码使用;而PSP(进程堆栈指针)用于常规的应用代码(即不处于异常服务例程中时)。
- R14为连接寄存器(即LR),主要作用就是保存子程序的返回地址,以便在执行完子程序时恢复现场;
- R15为程序计数器(即PC,这个如果不知道可以放弃编程了,哈哈),可以在程序里直接插入PC以跳转到相应的地址(不过不更新LR),所以功能大大滴有用。另外由于CortexM系列采用指令流水线技术,所以如果读PC返回值的话应该是当前指令的地址+4的,这个需要注意一下。
- 特殊功能寄存器组
- 程序状态寄存器(xPSR),程序状态寄存器在其内部又被分为三个子状态寄存器:
- 应用程序状态寄存器( APSR)
- 中断状态寄存器( IPSR)
- 执行状态寄存器( EPSR)
- 中断屏蔽寄存器组
- PRIMASK,只有1个位的寄存器。当它置1时,就关掉所有可屏蔽的异常,只剩下 NMI和硬fault可以响应。它的缺省值是0,表示没有关中断。
- FAULTMASK,只有1个位的寄存器。当它置1时,只有NMI才能响应,所有其它的异常,包括中断和fault,通通闭嘴。它的缺省值也是 0,表示没有关异常。
- BASEPRI ,8位寄存器(由表达优先级的位数决定)。它定义了被屏蔽优先级的阈值。当它被设成某个值后,所有优先级号大于等于此值的中断都被关(优先级号越大,优先级越低)。但若被设成0,则不关闭任何中断,0是缺省值。
- 控制寄存器
- CONTROL[2]
- 0:FPU禁止
- 1:FPU激活
- CONTROL[1]
- 0:选择主堆栈指针 MSP(复位后默认值)
- 1:选择进程堆栈指针 PSP
- 线程模式可以使用PSP。
- handler模式下,只允许使用MSP,所以此时不得往该位写1。
- CONTROL[0]
- 0:特权级的线程模式
- 1:用户机的线程模式
- Handler模式永远都是特权级的
- CONTROL[2]
- 程序状态寄存器(xPSR),程序状态寄存器在其内部又被分为三个子状态寄存器:
另外除了通用寄存器之外,CM4的内核还包括三组寄存器即程序状态寄存器组(PSRs)、中断屏蔽寄存器组(PRIMASK、FAULTMASK以及BASEPRI)和控制寄存器(Control):
nrf52832是Nordic基于Cortex-M4内核的蓝牙SOC,如下图:
Cortex-M4流水线
Cortex-M4使用一个三级流水线,分别是取指、译码和执行
读取PC会返回当前指令地址+4,以兼容Thumb代码。 不管是执行16位指令还是32位指令, 其偏移量总是4,这保证了Thumb和Thumb-2指令之间的一致性。
内核的预取单元中有一个指令缓冲区,它允许后续的指令在执行前先在里面排队,也能在执行未对齐的32位指令时,避免流水线“断流”。
缓冲区并不会在流水线中添加额外的级数,因此不会恶化跳转导致的性能下降( penalty)
Cortex-M4的总线
- I-Code总线(取指令)
- D-Code总线(取数据)
- 系统总线
Cortex-M4操作模式
Cortex-M4支持2个模式和两个权限等级:
当处理器处在线程状态下时,既可以使用特权级,也可以使用用户级;
handler模式总是特权级的。在复位后,处理器进入线程模式+特权级
在特权级下的代码可以通过置位 CONTROL[0]来进入用户级。用户级下的代码不能再试图修改 CONTROL[0]来回到特权级。
而不管是任何原因产生了任何异常,处理器都将以特权级来运行其服务例程,异常返回后将回到产生异常之前的级别。
用户级变更为特权级的方式:用户级下的代码必须通过一个异常 handler,由那个异常 handler 来修改 CONTROL[0],才能在返回到线程模式后拿到特权级。
Cortex-M4复位序列
Cortex-M4内核复位后,读取下列两个32位整数的值:
- 从地址 0x0000,0000 处取出 MSP 的初始值。
- 从地址 0x0000,0004 处取出 PC 的初始值——这个值是复位向量, LSB 必须是 1。然后从这个值所对应的地址处取指。
传统的ARM 架构总是从0地址开始执行第一条指令。它们的0地址处总是一条跳转指令。在 CM4中,0地址处提供 MSP 的初始值,然后就是向量表(向量表在以后还可以被移至其它位置)。向量表中的数值是32位的地址,而不是跳转指令。向量表的第一个条目指向复位后应执行的第一条指令
https://zhuanlan.zhihu.com/p/102395838
http://www.51hei.com/bbs/dpj-36588-1.html
https://www.kanzhun.com/jiaocheng/261366.html
https://blog.csdn.net/lucky534528795/article/details/88958337
https://wenku.baidu.com/view/b591fb01e87101f69e3195df.html