cortex-M3/cortex-M4 架构
cortex-m3 和 cortex-m4 处理器有两种操作状态和两种模式。特权访问级别可以访问处理器中的所有资源,而非特权访问级别则意味着无法访问某些内存区域以及无法使用某些操作。cortex-m不支持ARM指令集,因此没有ARM状态。cortex-m处理器启动后默认处于特权线程模式和Thumb状态。cortex-m3和cortex-m4处理器的寄存器集中共有16个寄存器,其中13个为32位通用寄存器,另外3个为特殊用途,如下图所示:。R0-R12:为通用寄存器,前8个称为低位寄存器。对于 cortex-m 处理器,PUSH 和 POP 始终是 32 位,并且堆栈操作地址也必须在 32 位字边界上对齐。置位后,它会阻止除 NMI 和 HardFault 异常之外的所有异常。
1 编程模型 1.1 运行模式及状态
cortex-m3 和 cortex-m4 处理器有两种操作状态和两种模式。 此外,处理器还可以区分特权和非特权访问级别,如下图所示。 特权访问级别可以访问处理器中的所有资源,而非特权访问级别则意味着无法访问某些内存区域以及无法使用某些操作。
运行状态:
调试状态:当处理器暂停时(例如,通过调试器或触发断点后),将进入调试状态并且指令执行停止。
Thumb状态:如果处理器正在执行程序代码(Thumb指令),则它将处于Thumb状态。 cortex-m不支持ARM指令集,因此没有ARM状态。
操作模式:
处理方式:执行中断服务程序等异常操作。 在进程模式下,处理器始终具有特权访问级别。
线程模式:在执行普通应用程序代码时,处理器可以处于特权或非特权访问级别。 实际的访问级别由特殊寄存器CONTROL 控制。
软件可以将处理器从特权线程模式切换到非特权线程模式,但它不能将自身从非特权模式切换到特权模式。 如果必须执行此切换,处理器必须使用异常机制。 除了内存访问权限和一些指令的差异外,特权和非特权访问级别的编程模型基本相同。 需要注意的是,几乎所有的NVIC寄存器都只支持特权访问。 同样,线程和处理的编程模型也类似。 然而,线程模式可以切换为使用单独的影子堆栈指针(SP)。 这种设计使得应用任务的堆栈空间独立于操作系统内核的堆栈空间,从而提高了系统的可靠性。
cortex-m处理器启动后默认处于特权线程模式和Thumb状态。 对于许多简单的应用程序来说,根本不使用非特权线程模式和影子SP。 cortex-m0 处理器不支持非特权线程模式,它在 m0+ 上是可选的。
调试状态仅用于调试操作,进入调试状态有两种方式:由调试器发起的暂停请求,或者由处理器中的调试组件生成的调试事件。 在此状态下,调试器可以访问或修改处理器寄存器的值。 无论是在Thumb状态还是调试状态,调试器都可以访问系统内存,例如处理器内部和外部的外设。
1.2 寄存器
cortex-m3和cortex-m4处理器的寄存器集中共有16个寄存器,其中13个为32位通用寄存器,另外3个为特殊用途,如下图所示:
R0-R12:为通用寄存器,前8个称为低位寄存器。 由于指令中的可用空间有限,许多 16 位指令只能访问低位寄存器。 高位寄存器可用于32位指令和多个16位指令,例如MOV(移动)。 R0~R12的初始值未定义。
R13:堆栈指针(SP),可以通过PUSH和POP操作实现对堆栈存储的访问。 物理上有两个堆栈指针,主堆栈指针(MSP 或 SP_main)是默认堆栈指针,处理器在复位后或处理器处于处理模式时会选择该指针。 另一种堆栈指针称为进程堆栈指针(PSP或SP_process),它只能在线程模式下使用。 堆栈指针的选择由特殊寄存器CONTROL 决定。 对于一般程序来说,这两个寄存器只有其中之一是可见的。
MSP和PSP都是32位的,但堆栈指针的最低两位始终为0,对这两位的写操作不起作用。 对于 cortex-m 处理器,PUSH 和 POP 始终是 32 位,并且堆栈操作地址也必须在 32 位字边界上对齐。 PSP的初始值没有定义,复位过程中需要从存储器的第一个字中取出MSP的初始值。
R14:链接寄存器(LR),用于保存函数或子程序调用时的返回地址。 在函数或字程序结束时,程序控制可以通过将 LR 的值加载到程序计数器 (PC) 中返回到调用程序并继续执行。 当执行函数或子程序调用时,LR 的值会自动更新。 如果函数需要调用另一个函数或子程序,则需要先将LR的值保存到堆栈中,否则函数调用执行后LR的当前值将丢失。 在异常处理过程中,LR也会自动更新为一个特殊的EXC_RETURN(异常返回)值,然后在异常处理结束时触发异常返回。 虽然cortex-m处理器中的返回地址值总是偶数(bit 0为0,因为指令会与半字地址对齐),但LR的bit 0是可读可写的,并且一些跳转/调用操作需要设置LR 的位 0 变为 1 以指示 Thumb 状态。
R15:程序计数器(PC),可读可写,读操作返回当前指令地址+4(由于设计的流水线特性以及需要兼容ARM7TDMI处理器)。 写入PC会引起跳转操作。 由于指令必须与半字或字地址对齐,因此PC的最低有效位(LSB)为0。但是,当使用某些跳转/读存储器指令更新PC时,需要设置新的PC值LSB为 1 表示 Thumb 状态,否则会因尝试使用不支持的 ARM 指令(如 ARM7TDMI 中的 32 位 ARM 指令)而触发 Error 异常。 对于高级编程语言,编译器会自动设置跳转目标的LSB。
1.3 特殊寄存器
除了寄存器堆中的寄存器外,处理器中还有几个特殊寄存器。 特殊寄存器不是内存映射的,可以使用特殊寄存器访问指令(例如 MSR 和 MRS)进行访问。 CMSIS-CORE 还提供了几个用于访问特殊寄存器的 C 函数。
MRS ,读特殊寄存器到寄存器
MSR,写特殊寄存器
程序状态寄存器
程序状态寄存器包含以下3个状态寄存器。 这三个寄存器可以通过组合寄存器 XPSR 进行访问。
应用PSR (APSR)
执行 PSR (EPSR)
中断 PSR (IPSR)
合并后的xpr内容如下:
N:负号
Z:零符号
C:进位(或非借位)标志
V:溢出标志
问:饱和标志
GE[3:0]:大于等于flag,对应各个字节通道
ICI/IT:中断继续指令(ICI)位,IF-THEN指令状态位用于条件执行。
T:Thumb状态,始终为1,清除该位将导致错误异常
异常编号:指示处理器正在处理的异常。
PRIMASK、FAULTMASK 和 BASEPRI 寄存器
PRIMASK、FAULTMASK 和 BASEPRI 寄存器用于异常或中断屏蔽。 每个异常(包括中断)都有一个优先级,值小的优先级高。 这些特殊寄存器可以根据优先级屏蔽异常,并且只能在特权访问级别进行操作(非特权状态下的写操作将被忽略,读将返回0)。 默认都是0,即屏蔽不起作用。
PRIMASK 寄存器是一个 1 位宽的中断屏蔽寄存器。 置位后,它会阻止除 NMI 和 HardFault 异常之外的所有异常。 实际上,它将当前异常优先级提高到 0,这是可编程异常/中断的最高优先级。
FAULTMASK与PRIMASK类似,但它也可以屏蔽HardFault异常,实际上将异常优先级提升到-1。
BASEPRI 根据优先级屏蔽异常或中断。
CMSIS-CORE 提供了许多用于访问这些特殊寄存器的 C 函数(只能在特权级别访问)。
x= _get_BASEPRI() //读BASEPRI寄存器
x= _get_PRIMASK() //读PRIMASK寄存器
x= _get_FAULTMASK() ///读FAULTMASK寄存器
_set_BASEPRI(X) //设置BASEPRI寄存器的新数值
_set_PRIMASK(X) //设置PRIMASK寄存器的新数值
_set_FAULTMASK(X) //设置FAULTMASK寄存器的新数值
_disable_irq();//设置PRIMASK,禁止IRQ
_enable_irq();//清除PRIMASK,使能IRQ
这些异常屏蔽寄存器也可以使用汇编代码访问
MRS r0 BASEPRI //将BASEPRI寄存器读入R0
MRS r0 PRIMASK //将PRIMASK寄存器读入R0
MRS r0 FAULTMSK //将FAULTMSK寄存器读入R0
MSR BASEPRI RO //将R0值写入BASEPRI寄存器
MSR PRIMASK RO //将R0值写入PRIMASK寄存器
MSR FAULTMSK RO //将R0值写入FAULTMSK寄存器
另外,可以通过修改处理器状态CPS命令方便地设置或清除PRIMASK和FAULTMASK的值
CPSIE i //使能中断,清除PRIMASK
CPSID i //禁止中断,设置PRIMASK
CPSIE f //使能中断,清除FAULTMASK
CPSID f //禁止中断,设置FAULTMASK
控制寄存器
控制寄存器的定义如下图所示
nPRIV:定义线程模式下的权限级别。 当它为0(默认)时,处理器将处于线程模式下的特权级别,当它为1时,它将处于线程模式下的非特权级别。 该位在 cortex-M0 中不存在,在 cortex-M0+ 中是可选的。
SPSEL:定义堆栈指针的选择。 当它为0(默认)时,线程模式使用主堆栈指针MSP。 为1时,线程模式使用进程堆栈指针; 在处理模式下,该位始终为0并且被写入。 将被忽略。
FPCA:浮点上下文活动,仅存在于具有浮点单元的 CORTEX-M4 中。 异常处理机制通过该位来决定异常发生时是否需要保存浮点单元中的寄存器。 当为1时,当前上下文使用浮点指令,因此需要保存浮点寄存器。 FPCA 位在执行浮点指令时自动置位,并在异常进入时由硬件清零。
复位后,CONTROL寄存器默认为0,这也意味着处理器现在处于线程模式,具有特权访问,并使用主堆栈指针。 通过写入 CONTROL 寄存器,特权线程模式下的程序可以切换堆栈指针选择或进入非特权访问级别。 然而,设置nPRIV后,在线程模式下运行的程序无法访问CONTROL寄存器。
在非特权级别运行的程序无法切换回特权访问级别,从而提供了基本的安全模型。 例如,嵌入式系统可能有运行在非特权访问级别的不受信任的应用程序,需要限制这些应用程序的访问权限,以防止不可靠的程序导致整个系统崩溃。 如果需要在线程模式下将处理器切换回特权访问级别,则需要使用异常机制。 在异常处理过程中,处理程序可以清除nPRIV位,如下图所示,返回线程模式后,处理器进入特权访问级别。
如果使用嵌入式操作系统,每次上下文切换时,CONTROL寄存器都可以重新编程,以满足应用程序之间不同特权访问级别的需要。
nPRIV 和 SPSEL 设置有 4 种组合,其中 3 种在实际应用中较为常见。
nPRIVSPSEL应用场景
简单应用程序,整个应用程序运行在特权访问级别,主程序和中断处理只会使用一个堆栈,主堆栈
对于嵌入式操作系统的应用程序,当前执行的任务运行在特权线程模式下,当前任务选择使用进程堆栈指针(PSP),操作系统内核和异常处理使用MSP。
对于嵌入式操作系统的应用程序,当前执行的任务运行在非特权线程模式下,当前任务选择使用进程堆栈指针(PSP),操作系统内核和异常处理使用MSP。
线程模式运行在非特权访问级别,并使用MSP,这在进程模式下是可见的,而用户任务一般不可用,因为在大多数嵌入式IS中,应用程序任务的堆栈与OS内核和异常处理使用的堆栈进行交互。 独立的。
控制寄存器访问方法:
x = _get_CONTROL(); //读取CONTROL寄存器的当前值
_set_CONTROL(X); //设置CONTROL寄存器的数值x
MRS r0,CONTROL //将CONTROL寄存器读入R0
MSR CONTROL, r0 //将r0写入CONTROL寄存器
修改CONTROL寄存器需要修改以下两点:
① 对于带有浮点单元(FPU)的 M4 处理器,或者带有 FPU 的 ARMv7-M 处理器,由于浮点单元的存在,FPCA 位被自动置位。 如果程序中包含浮点运算,但意外清零了 FPCA 位,然后产生中断,则在异常进入过程中浮点单元寄存器中的数据将不会被保存,并且可能会被中断处理覆盖。 在这种情况下,当中断的任务恢复时,程序可能无法继续正确处理。
②修改CONTROL寄存器后,从体系结构的角度来看,应该使用指令同步屏障(ISB)指令来确保这次修改可以对下一个代码起作用。 由于M3 M4 M0 M0+ M1管线非常简单,所以没有必要使用ISB 不会造成任何问题。
2. 应用状态寄存器 2.1 整型状态标志
整数状态标志与许多其他处理器架构中的 ALU 状态标志类似,它们受常见数据处理指令的影响,并且在控制条件跳转和条件执行方面很有用。 cortex-m处理器中有4个整数标志,如下:
标志说明
设置为执行指令结果的位[31]。 为1时,结果为负值; 为0时,结果为正或0。
如果执行指令的结果为0,则设置为0。如果执行比较指令,发现两个值相等,则该位也设置为1。
结果进位标志,对于无符号加法,如果发生无符号溢出,则该位设置为1; 对于无符号减法,该位与借位输出的状态相反,移位和循环移位也会影响该位
结果溢出了。 对于有符号加法或减法,如果发生有符号溢出,则该位设置为 1
对于 ARMv7-M 和 ARMv7E-M 架构,大多数 16 位指令会影响这 4 个 ALU 标志。 对于大多数 32 位指令,指令编码中的一位定义是否应更新 APSR 标志。 注意,有些指令不会更新V和C标志,例如MULS指令只会修改N和Z标志。
2.2 Q状态标志
饱和标志
2.3 GE bit 3. 内存系统 3.1 内存系统特性
cortex-m3 和 cortex-m4 处理器具有以下内存系统功能:
①4GB线性地址空间。采用32位寻址,ARM处理器最多可访问4GB内存空间
② 架构定义的内存映射。 4GB 内存空间分为预定义内存和外设区域,以优化处理器设计的性能。
③支持小端和大端存储系统。
④位段访问(可选) 当包含位段功能时,存储器映射的两个 1MB 区域可以通过两个位段区域进行寻址,从而可以对 SRAM 或外设地址空间中的各个位进行原子操作。
⑤ 写缓冲区。 如果对可缓冲存储器区域的写传输需要几个周期,则M3 M4处理器中的写缓冲器可以缓存该传输,并且处理器可以继续执行下一条指令(如果可能),这可以提高程序性能。 执行速度。
⑥内存保护单元(MPU).mpu定义了各个内存区域的访问权限,并且是可编程的。
⑦ 不对齐传输支持。 ARMv7-M 架构的所有处理器都支持费用调整传输。
3.2 内存映射
cortex-m处理器的4GB地址空间被划分为多个内存区域,如下图所示,这些区域根据其典型用途进行划分,主要用于:
程序代码访问(如CODE区)
数据访问(如SRAM区域)
周边(如外围区域)
处理器的内部控制和调试组件(例如专用外设总线)
3.3 堆栈存储
与几乎所有处理器架构一样,cortex-M 处理器在运行时需要堆栈存储和堆栈指针 (R13)。 在栈的内存使用机制中,可以将一部分内存作为后进先出的数据存储缓冲区。 ARM处理器使用系统主存进行堆栈空间操作,使用PUSH指令在堆栈中存储数据,使用POP指令从堆栈中取出数据。 每次PUSH和POP操作后,当前使用的堆栈指针都会自动调整。
堆栈可用于:
① 当正在执行的函数需要使用寄存器(寄存器组中)进行数据处理时,暂时存储数据的初始值。 这些数据可以在函数结束时恢复,这样调用该函数的程序就不会丢失数据。
② 将信息传送到函数或子程序。
③ 用于存储局部变量。
④ 当发生中断等异常时,保持处理器状态和寄存器值。
Cortex-M 处理器使用的堆栈模型称为全递减。 处理器启动后,SP被设置为堆栈存储空间中的最后一个位置。 对于每次PUSH操作,处理器首先递减SP的值,然后将数据存储到SP指向的内存位置。 在操作过程中,SP指向堆栈中最后存储数据的位置。 对于POP操作,读取SP指向的内存位置的数据,然后SP的值自动递增。
应用场景一示例:
应用场景2示例:
应用场景3示例:
4. 例外和中断
异常是改变程序流程的事件。 当它发生时,处理器挂起当前正在执行的任务并执行一个称为异常处理的程序。 执行异常处理后,处理器继续正常程序执行。 对于ARM架构来说,中断是异常的一种,一般由外设或外部输入产生,有时也可以由软件触发。 中断的异常处理也称为中断服务程序。
Cortex-m 处理器有多个异常来源。
① NVIC处理异常。 NVIC 可以处理多个中断请求(IRQ)和一个不可屏蔽中断(NMI)请求。 IRQ一般由片内外设或通过I/O端口输入的外部中断产生。 NMI 可用于看门狗定时器或掉电检测。 处理器内部还有一个系统定时器。
②处理器本身也是异常事件的来源,包括指示系统错误状态的错误事件以及软件和支持嵌入式操作系统操作产生的异常。 这些例外情况如下表所示:
异常编号 CMSIS 中断编号 异常类型 优先级 功能
—
重置
-3(最高)
重置
-14
国家管理研究所
-2
国家管理研究所
-13
硬件错误
-1
对于所有类的错误,如果由于异常掩码禁用或阻止而未激活相应的错误处理,则会引发此异常
-12
内存管理错误
可设定
内存管理错误,由 MPU 冲突或非法访问(例如从非执行区域获取指令)引起
-11
总线错误
可设定
从总线系统收到的错误响应,由指令预取终止和数据访问错误引起
-10
使用错误
可设定
使用错误,通常是由于非法指令或非法状态转换尝试(例如尝试切换到 M3 中的 ARM 状态)
7~10
—
—
—
预订
11
-5
SVC
可设定
通过 SVC 请求管理呼叫
12
-4
调试监控
可设定
基于软件的调试的调试监控
13
—
—
—
预订
14
-2
彭德西夫
可设定
待处理的系统服务请求
15
-1
系统棒
可设定
滴答定时器
16~255
0~239
中断请求
可设定
中断请求
NVIC是cortex-m处理器的一部分,它是可编程的,寄存器位于内存映射的系统控制空间中。 NVIC处理异常和中断配置、优先级和中断屏蔽,具有以下特点:
灵活的异常和中断管理
支持嵌套异常/中断
向量异常/中断入口
中断屏蔽
当异常事件产生并被处理器核接受时,就会执行相应的异常处理。 为了确定异常处理的起始地址,处理器利用向量表机制。 向量表是系统内存中的字数据数组,每个元素代表异常类型的起始地址。 向量表是可重定位的,重定位由NVIC中称为向量表偏移寄存器VTOR的可编程寄存器控制。 复位后,VTOR默认为0,向量表位于地址0。
5、复位及复位流程
对于典型的 CORTEX-M 微控制器,有三种复位类型
① 上电复位:复位微控制器的所有部分,包括处理器、调试支持组件和外设等。
②系统复位:仅复位处理器及外设,不包括处理器的调试支持组件
③处理器复位:仅复位处理器
复位后、处理器开始执行程序之前,cortex-m 处理器从内存中读取前两个字,如下图所示。 向量表位于存储器的开头,它的前两个字是主堆栈指针MSP的初始值,以及代表复位过程起始地址的复位向量。 处理器读取这两个字后,会将这些值赋给MSP和程序计数器PC。
MSP的设置是非常有必要的,因为复位短时间内有可能出现NMI或者HardFault,并且在异常处理之前将处理器状态压入堆栈时需要堆栈存储和MSP。
对于大多数C开发环境,C启动代码在进入主程序main之前会更新MSP的值。 通过两次设置堆栈,具有外部存储器的微控制器可以将外部存储器用作堆栈。
由于M3和M4的堆栈操作是基于全减堆栈(SP先减后存储),因此SP的初始值应设置为堆栈区域顶部的第一个位置。 例如,如果内存区域为0x20007C00 ~ 0X20007FFF,则初始堆栈指针应为0x20008000。
备注:请参考ARM Cortex-M3和Cortex-M4的权威指南