Cortex-M4内核结构
Cortex-M4处理器是ARMv7-M架构的一种实现,它是一种32位精简指令集(Reduced Instruction Set Computing, RISC)的处理器,有一个三阶的指令流水线,依次对指令进行取指、 解码、执行操作。它是一种哈佛架构,可以同时获取指令和数据,使用32位的地址总线,可以访问4G的地址空间。而且它采用的是一种装载存储结构, 也就是说它不能直接操作内存空间里的数据,需要先把内存空间中的数据装载到内核寄存器中,处理完毕之后再存储到内存中。
右图是从《M3M4权威指南》的第62页中截取的Cortex-M4的内核结构框图。 大体上可以分为三个部分:内核、总线接口、调试系统。其中调试系统主要用于固件的调试,监视系统的运行状态。它对于我们应该如何编写程序没有影响,本文不介绍它。
内核是处理器最核心的部分。它完成了几乎所有的运算、控制着程序的运行过程,以及中断的响应服务。它通过总线接口与内存空间交互,获取指令,装载数据,驱动外设。
内核与总线接口将是我们频繁打交道的两个主要部分,对它们的理解有助于我们更好的实现嵌入式操作系统XiaoTuOS。本文将详细介绍这两个部分。
1. 内核Core
根据右边的图示,内核由5个部分构成: 中断控制器(NVIC)、系统计时器(SysTick)、三阶流水线(3-stage pipeline)、浮点单元(FPU)、指令跟踪接口(Instruction Trace Interface)。 其中指令跟踪接口主要用于调试,不再解释。
中断控制器(NVIC)全称是Nested Vectored Interrupt Controller。它是处理器用于响应一些特定的事件的系统,这些特定的事件包括UART接收到了一个字节、计时器溢出等等。 每当有中断信号产生的时候,处理器就会先停下当前的任务,来处理中断事件,处理完毕之后再恢复到原来的任务当中。NVIC用一个中断向量表来记录各个中断服务函数的入口, 所以说它是Vectored。另外在Cortex-M4中中断是可以嵌套的,当处理一个低优先级的中断事件过程中,产生了一个高优先级的中断,处理器将优先处理高优先级的中断, 处理完毕之后的恢复过程也是先恢复低优先级的处理程序,再恢复到原先的系统任务。所以说这个控制器是Nested。
系统计时器(SysTick)是Cortex-M4内核中的一个计时器。它与程序的运行过程、中断的控制没有任何关系。我们完全可以将它看作是一个计时器外设。 很多在Cortex-M4上的操作系统都会用它来实现时钟系统。也不是必须用它来实现,只是它具有比较高的可移植性。只要是Cortex-M4的内核,甚至是Cortex-M系列的内核,都可以直接移植, 而不必再额外的提供系统时钟。
浮点单元(FPU)是Cortex-M4内核的一个可选项,支持单精度的浮点运算。它就是一个协处理器,具有自己独立的寄存器组、执行解析和执行流水线,以及对应的浮点运算指令集。 关于浮点单元的使用,我们将有专门的文章介绍。
三阶流水线(3-stage pipeline)可以说是处理器内核的核心。下面我们专门用一节介绍它。
2. 三阶流水线
如左图所示,Cortex-M4执行一条加法指令,将寄存器R1和R3中的数据相加,并把结果放到R2寄存器的计算过程。每条指令都需要经过3个阶段:Fetch、Decode、Execute。
取指令(Fetch)是内核通过内存接口(memory interface),获取一条指令的过程。在这个过程中,内核根据在“寄存器银行”(Register Bank)中的指令指针寄存器PC, 来确定需要索取的指令所在内存中的位置。内存接口则需要通过处理器上的各种总线,比如I-Code,来从内存中把指令搬到内核中。
解码(Decode)所完成的工作就是解析索取的指令。我们知道在计算机的世界中只有'0'和'1'。那么从内存中索取的一串'0'和'1'的序列具体是一个什么指令呢? 这就是解码器要做的工作。这个'0','1'序列的具体含义,就是处理器的指令集需要定义的事情。
结果解码之后,内核就知道它接下来需要执行一个加法任务。加法的两个操作数分别保存在寄存器R1和R3中,计算的结果需要保存到R2中。这三个寄存器都是Register Bank中的通用寄存器。剩下要做的就是执行(Execute)这一任务。在执行过程中,除了要按照指令要求保存计算结果之外,处理器还需要根据执行的结果更新系统的状态寄存器PSR。 PSR中有很多标志位,反映了该次计算结果是否溢出、是否为零等状态。这些状态是程序用来判定条件语句是否为真的依据,进而可以控制程序的指令流程。
假设,我们有一条if (a == b)
的条件语句,在汇编层面上,我们可以将它分为几个步骤来实现: 首先,把变量a和b分别装在两个通用寄存器R1和R3中,
然后通过指令SUB R2, R1, R3
来对a和b求差,结果将放到寄存器R2中。最后通过条件跳转语句BNE来查询PSR中的条件标识Z,判定SUB的计算结果是否为0,
如果不为零,则说明两个数据不相等,将跳过if之后的语句。当然这种分支语句的具体实现取决于编译器,但大体的套路就是这样的。
每条语句都需要经过取指、解码、执行三个操作才能完成,也就是说最快也需要三个时钟周期才能运行一条语句。但是因为有流水线的操作,处理器可以在执行一条语句的同时, 对其它语句进行取指和解码操作,如下图1所示。那么在没有任何分支语句或者跳转的情况下,每条语句的执行就只需要1个时钟周期就可以完成。
图1 Cortex-M4流水线过程示意图 |
3. 内核工作模式
为了满足系统的不同Cortex-M4还有一些不同的工作模式,并且在不同的工作模式下它具有privileged和unprivileged两种访问权限。其中privileged可以访问处理器的所有资源, 而unprivileged则对一些指令和内存空间不能访问。在一些资料中把具有unprivileged级访问权限的状态和模式称为用户态"User state"。 为系统划分不同的权限等级,使得开发人员能够根据需要保护一些内存和资源不被随意地访问和修改,提高了系统的安全性和鲁棒性。
正常工作时,Cortex-M4有Handler和Thread两种工作模式。当处理器进入中断服务函数,处理系统异常或者中断事件时,就会进入Handler模式。 在Handler模式下,系统总是具有privileged级访问权限。一般情况下处理器都是Thread模式,在该模式下系统的访问权限可以是privileged也可以是unprivileged,具体由控制寄存器决定。 在Thread模式下,应用程序可以自由地由privileged切换到unprivileged,但是反过来则不可以。 如果必须要从unprivileged切换到privileged,则需要借助异常和中断处理机制。
如下图2所示,我们的操作系统可以看作是工作在Privileged Thread模式和Handler模式下的软件,它具有比较高级的资源访问权限,可以管理系统资源。与外设驱动息息相关的中断服务, 也应当是操作系统内核的一部分。用户的应用程序则工作在Unprivileged Thread模式下,当由应用程序需要访问系统资源时,需要通过操作系统提供的系统调用来实现。
图2 Cortex-M4内核工作模式和权限示意图 |
此外,Cortex-M4有Debug和Thumb两种工作状态。当调试器触发暂停或者运行到断点时,处理器就会进入Debug状态停止执行指令。 运行时加入断点或者单步调试,是一种常用的白盒调试方法,可以直接关注内存。处理器执行指令时就处于Thumb状态。ARM内核一般都支持Thumb和ARM两种指令集, 但是Cortex-M系列只支持Thumb指令集,因此只有Thumb状态。
4. 总结
本文中,我们简单介绍了Cortex-M4的内核结构,它是一种32位的具有哈佛架构的精简指令集处理器。它使用一种三阶流水线的方式来执行指令,每条指令都将经过取指、解码、执行三个操作, 并且在执行一条指令的同时可以对其它指令进行取指和解码操作,这样可以提高系统的运行效率。
此外,Cortex-M4可以工作在不同的模式下,这些各种不同的模式,具有privileged和unprivileged的访问权限。其中privileged的访问权限可以访问处理器中的所有资源, 通常操作系统具有这种权限。而unprivileged的访问权限常常用于用户的应用程序。