1、前言
Arm Cortex-M家族已经有12款处理器,涵盖了ARMv6-M, ARMv7-M和ARMv8-M架构。CoreMark从1.82到6.28,DMIPS从0.88到3.13。
在Cortex系列之前,Arm其实也有开发了一些处理器,只不过后面按照市场的不同需求,拆为三种新的架构来分别满足高性能通用计算(Cortex-A)、实时高可靠(Cortex-R)、低功耗微控制(Cortex-M)三大类需求。
Cortex-A: 主要用于应用处理器、智能手机、服务器等,它们的时钟频率一般大于1GHz,而且支持MMU。因此Cortex-A可以支持OS的全部特性,比如Linux、Android等。
Cortex-R: 主要用于实时性较强的应用,比如硬盘控制器、汽车动力系统、工业微控制器和无线基带等,它们的时钟频率一般200MHz~1GHz。它们大多数没有使用MMU,而是使用MPU。因此Cortex-R不能直接跑Linux,它们通常运行RTOS。
Cortex-M: 主要用于低面积、低功耗和更高的能源效率场合,常用于微控制器和嵌入式系统,比如传感器、IC卡和IoT等。它们的时钟频率一般小于200MHz,pipeline比较短。
Cortex-M相比于更老的ARM处理器(比如ARM7TDMI, ARM9),在架构上做了很多的改动,比如只支持Thumb指令集、引入NVIC来管理中断和系统异常、可以使用C语言编写异常处理程序等。
Cortex-M架构现在已经演进到Armv8.1,在Armv8.1中核心是Helium(MVE)向量扩展,使之具备DSP的功能。
Arm公司现在应该不会发布新的基于ARMv6-M和ARMv7-M架构处理器,所以基于v6和v7的处理器目前也就6种:Cortex-M0,Cortex-M0+,Cortex-M1,Cortex-M3,Cortex-M4和Cortex-M7。离现在最近的Cortex-M7也已经上市十多年了,所以我称这些Cortex-M处理器为老古董。本文主要聊聊这些老古董的发展历史。
不同的Cortex-M处理器支持不同指令集的子集,如下图所示。
Cortex-M0/M0+/M1是基于ARMv6-M架构,只支持56条指令,而且大多数是16-bit编码的,因此它们面积是非常小、功耗非常低的,特别Cortex-M0/M0+就12K个门电路。但受限于16-bit指令编码,无法充分利用高编号寄存器(R8~R12)。M0的对外接口是AHB-lite,因此对I/O操作至少2个周期,M0+是在M0的基础上新增单周期I/O操作能力和vector table重定位能力。M1是为了FPGA设计的,它提供了TCM功能。
Cortex-M3是基于ARMv7-M开发的,它支持更多32-bit的指令集,因此可以有效利用高编号寄存器。另外,它还支持Table branch指令、TT指令、硬件除法指令、MAC和bit field处理指令,因此M3具备了基本的数字处理操作。更丰富的指令集会带来更大的芯片面积和更高的能耗,M3 的门电路数量比 Cortex-M0 或 Cortex-M0+ 的设计多出两倍以上。
Cortex-M4和Cortex-M3很像,特别是pipeline和programmer model,而且具备了M3的所有功能,它还支持DSP应用中的各种指令,例如SIMD指令、饱和算术指令、一系列可在单个周期内执行的MAC指令,以及一个可选的浮点单元,来支持IEEE 754标准的单精度浮点运算。M4的SIMD操作能够同时处理两个16-bit数据或4个8-bit数据,如下图QADD8 和 QADD16 这两种运算:
Cortex-M7包含了Cortex-M4的所有特性,另外支持双精度浮点计算,而且在硬件是也支持TCM和Cache,因此它的性能还是很高的,CoreMark达到5.29,DMIPS达到2.31。
Cortex-M 处理器中ISA的一个关键特性是向上兼容性:Cortex-M3 处理器所支持的指令集包含了是 Cortex-M0/M0+/M1 指令集。因此,理论上如果内存映射相同,Cortex-M0/M0+/M1 的二进制文件可以直接在 Cortex-M3 上运行。同样的情况也适用于 Cortex-M4/M7 与其他 Cortex-M 处理器之间的关系;Cortex-M0/M0+/M1/M3 上可用的指令可以在 Cortex-M4/M7 上运行。
2、Programmer model
Cortex-M 处理器系列的programmer model很类似。例如,R0 到 R15、PSR、CONTROL 和 PRIMASK 对所有 Cortex-M 处理器都是可用的。两个特殊的寄存器(FAULTMASK 和 BASEPRI)只在 Cortex-M3、Cortex-M4 和 Cortex-M7 上可用,而浮点寄存器组和 FPSCR(Floating Point Status and Control Register)则仅在 Cortex-M4/M7 内部的可选浮点单元中可用,如下图所示。
BASEPRI 寄存器允许屏蔽特定优先级或更低优先级的异常和中断。在 ARMv7-M 中这一点还是蛮重要的,因为 Cortex-M3、Cortex-M4 和 Cortex-M7 中可以有大量不同的优先级,而 ARMv6-M 中则仅限于四个程序级别。FAULTMASK 通常用于复杂的故障处理程序
在 ARMv6-M 中,非特权执行级别是可选的;而在 ARMv7-M 中,该级别始终可用。在 Cortex-M0+ 中,此功能是可选的,而在 Cortex-M0 和 Cortex-M1 中则不可用。因此不同型号的 Cortex-M 处理器中的CONTROL 寄存器可能存在细微的差别。浮点运算单元(FPU)选项也会对控制寄存器产生影响,如图下图所示。
Programmer model的另一个区别在于 PSR(Program Status Register)。在所有 Cortex-M 处理器中,PSR 可以细分为Application PSR、Execution PSR 和Interrupt PSR。在 ARMv6-M 中,APSR 中的 Q 位和 EPSR 中的 ICI/IT 位并不存在,而 GE 位仅在 ARMv7E-M 中可用(例如 Cortex-M4)。此外,ARMv6-M 中 IPSR 中中断编号的宽度范围较小,如下图所示。
3、Exception model
所有的 Cortex-M 处理器都配备了嵌套向量中断控制器(NVIC),并且它们采用相同的异常模型。当发生异常且其优先级高于当前级别,且未被任何屏蔽寄存器所阻塞时,处理器会接受该中断/异常,并将 8 个寄存器压入当前栈中。这种堆栈机制使得中断处理程序能够以普通的 C 函数形式编写。
在 ARMv7-M 中可用的一些中断和系统异常在 ARMv6-M 中并不适用,如下图所示。例如,在 Cortex-M0、M0+ 和 M1 中,中断的数量被限制在 32 个以内,没有Debug Monitor exception,fault exception仅限于HardFault。相比之下,Cortex-M3、Cortex-M4 和 Cortex-M7 处理器支持多达 240 个外设中断。
另一个不同之处在于可用的优先级级别数量。在 ARMv6-M 架构中,中断/异常优先级级别包含 2 个固定级别(用于 NMI 和 HardFault)以及 4 个可编程级别(每个优先级级别寄存器有 2 位)。在 ARMv7-M 架构中,可编程优先级级别数量从 8 个级别(3 位)到 256 个级别(8 位)不等。实际上,由于芯片面积的限制,大多数设备仅实现 8 个(3 位)到 16 个级别(4 位)的优先级设置。
所有 Cortex-M 处理器在处理异常时都依赖于向量表。向量表存储异常处理程序的起始地址,且默认情况下它位于内存映射的起始位置。可以通过VTOR(Vector Table Offset Register)来更改向量表的起始地址。VTOR 可在 Cortex-M0+/M3/M4/M7 以及 ARMv8-M 处理器上使用(在 Cortex-M0+ 和 ARMv8-M 版本中为可选功能)。
NVIC 的programmer model在不同的 Cortex-M 处理器之间也存在一些差异。这些差异如下图:
4、Fault handling
ARM 处理器与某些其他微控制器架构之间的一个区别在于其故障(Fault)处理能力。当检测到故障时,会触发故障异常,以便软件能够采取相应的措施。故障类型包括:
未定义的指令(例如闪存损坏)
对非法地址空间的访问(例如堆栈指针损坏)或 MPU 访问违规
非法操作(例如当处理器处于高于 SVC 的中断优先级时尝试触发 SVC 异常)
故障处理功能使嵌入式系统能够更迅速地应对各种问题。否则,如果系统出现故障,看门狗定时器可能需要很长时间才能使系统恢复正常运行。
在 ARMv6-M 架构中,所有的故障事件都会触发HardFault处理程序,该处理程序的优先级为 -1(高于所有可编程异常,但略低于不可屏蔽中断 NMI)。所有故障都被视为无法恢复的,通常我们会进行错误报告,并在硬故障处理程序内部可能生成自重启操作。
在 ARMv7-M 架构中,除了硬故障之外,还设有三个可配置的故障处理程序:
Memmanage (Memory Management Fault)
Bus Fault (Bus returns an error response)
Usage Fault (undefined instructions or other illegal operations)
这些异常具有可编程的优先级等级,并且可以单独启用/禁用。它们还可以利用FAULTMASK寄存器来提升其优先级,使其与HardFault具有相同的级别。此外,还有众多的故障状态寄存器,这些寄存器能够提供有关引发故障异常的原因,还可通过故障地址寄存器来确定引发故障的地址,从而调试更加容易。
ARMv7-M 中新增的故障处理程序提供了更灵活的故障处理能力,商业开发套件中的许多调试器都包含了利用故障状态寄存器来诊断故障事件的功能。此外,在运行时,故障处理程序还能够执行一些补救措施。
194
