不同于A系列的, M系列只有一组寄存器R0~R15; M4相对于M3多了一个浮点单元FPU, 其他的基本与M3是一样的。
一、从指令集方面看:
某些ARM处理器支持ARM和Thumb指令集,而Cortex-M3只使用Thumb-2指令集。ARM指令集是32位完整体系结构。Thumb指令可视为ARM指令压缩形式的子集,为16位指令集。THumb指令只需要支持通用功能,必要时,可借助完善的ARM指令集。
Thumb-2是一个突破性的指令集,是16位Thumb指令集的一个超集,在Thumb-2,16位指令首次与32位指令并存,这样子在Thumb状态下可以做的事情一下子丰富了许多,同样的工作需要的指令周期数也明显下降。因为它允许32位指令和16位指令水乳交融,代码密度和处理性能两手抓。
在以前做ARM开发必须处理好ARM状态和Thumb状态,且这两个状态是井水不犯河水。
当处理器在ARM状态下时,所有的指令均是32位的(哪怕只是个"NOP"指令),此时性能相当高。
而在Thumb状态,所有的指令均是16位的,代码密度提高了一倍。但thumb状态下的指令功能只是ARM下的一个子集,结果可能需要更多条的指令去完成相同的工作,导致处理性能下降。
取长补短,多数应用程序都混合使用ARM和Thumb代码段。---切换之际导致额外开销(时间上和空间上)。
且ARM代码和Thumb代码需要以不同的方式编译,这也增加了软件开发管理的复杂度。
主程序代码在Thumb下执行----->BLX 跳转到ARM态--->BX LR --->主程序代码在Thumb下执行。
Thumb-2指令集的出现,就可以在单一的操作模式下搞定所有处理了,没有了来回切换。事实上,Cortex-M3 内核干脆都不支持ARM指令,连中断也在Thumb态下处理(before ARM总在ARM态下处理所有的中断和异常)。这就使得CM3在多个方面比传统的ARM处理器更先进。
Thumb-2指令集
二、从寄存器方面看:
Cortex-M3/M4系列的内核拥有通用处理器R0~R15以及一些特殊功能寄存器。
R0-R12:通用寄存器;R0‐R12 都是 32 位通用寄存器,用于数据操作。
16位Thumb指令只能访问R0~R7(低组寄存器); 32为Thumb-2指令可以访问所有寄存器(low Registers 和 hight Registers)。
R13(SP指针) 主堆栈指针MSP 进程堆栈指针PSP 两个堆栈指针,即支持两个堆栈。PUSH指令(入栈)和POP指令(出栈)默认使用SP。
MSP:复位后缺省使用的堆栈指针,用于操作系统内核以及异常处理例程(包括中断服务程序)以及所有需要特权访问的应用程序代码
PSP:由用户的应用程序代码使用。
注:在ARM编程领域中,凡是打断程序顺序执行的事件,都被称为异常(exception).
除了外部中断外,还有执行了"非法操作"、访问被禁的内存区间、因各种错误产生的fault以及不可屏蔽中断 事件s。
R14(连接寄存器)——当呼叫一个子程序时,由R14存储返回地址
ARM为了减少访问内存的次数(访问内存的操作往往要3个以上指令指南,而带MMU和cache的九不确定了),把返回地址直接存储在寄存器中。只有1级子程序调用的代码无需访问内存(堆栈内存),从而提高子程序的调用效率。
多于1级的,则需要把前一级的R14值压倒堆栈里。——溅出
在ARM上编程时,应尽量只是用寄存器保存中间结果,迫不得已才访问内存。
在使用BL指针(Branch and Link)时,就把PC+1自动填充LR的值。因为PC的LSB总是0(因为代码至少是字对齐的), 而LR的LSB(最低有效位)的可读可写,这是历史遗留的产物。在before,用位0来指示ARM/Thumb状态,这是因为某些ARM处理器支持ARM和Thumb状态并存,为了方便汇编程序移植,CM3/CM4需要允许LSB可读可写。 实际使用时,并不看LSB,字对齐。
R15(PC)——程序计数寄存器 指向当前的程序地址。 如果修改它的值,就能改变程序的执行流(高级技巧)
特殊功能寄存器有预定义的功能,且必须通过专用的指令来访问。
M3/M4内部使用了指令流水线,因此读PC时返回的值是当前指令的地址+4
0x1000: MOV R0, PC; 此时R0 = 0x1004
CM3/CM4中的指令至少是半字对齐的,所以PC的LSB总是读回0.
在分支时,无论是直接写PC的值还是使用分支指令,都必须保证加载到PC的数值是奇数(即LSB=1),用以表明这是出于Thumb状态下执行的。
如果PC加载的数值为偶数(即LSB=0),视为企图转入ARM模式,CM3将产生一个fault异常。
特殊功能寄存器组
Cortex-M3/M4有一个特殊功能寄存器组,
--->程序状态寄存器组(PSRs/xPSR) 3个
--->中断屏蔽寄存器组(PRIMASK, FAULTMASK,以及BASEPRI)
--->控制寄存器(CONTROL)
这7个寄存器只能被专用的MSR和MRS指令访问,而且它们也没有存储器地址。
PSR包括应用程序PSR<APSR> 、中断号PSR<IPSR>、执行PSR<EPSR> 三个程序状态寄存器,
PRIMASK、FAULTMASK、MASEPRI这三个寄存器用于控制异常的使能和除能。
PRIMASK 这个寄存器只有一位,当该位为1时,就屏蔽所有可屏蔽的异常,除NMI和硬件fault可以响应。默认是0;
FAULTMASK这个寄存器也只有一位,置1时,只有NMI才能响应,所有其他异常均屏蔽。默认为0
BASEPRI 这个寄存器的有效位由表达优先级的位数决定,最多9位。它定义了被屏蔽优先级的阈值,当优先级号大于等于此值的中断均被关闭。默认为0,则不关闭任何中断。
对于关键任务,暂时关闭中断很重要,要处理PRIMASK和BASEPRI。
FAULTMASK则被OS用于暂时关闭fault处理机能。
开关中断的方法M3/M4
CPSID I; PRIMASK=1, ;关中断
CPSIE I; PRIMASK=0, ; 开中断
CPSID F; FAULTMASK=1, ; 关异常
CPSIE F; FAULTMASK=0, ; 开异常
CONTROL(控制寄存器)
用于定义特权级别和堆栈指针的使用(MSP/PSP)
位[31~3] | 位[2] | 位[1] | 位[0] |
保留 | M4特有,0未使用浮点单元 1使用浮点单元 以此位来决定当处理异常时是否保存浮点环境 | 堆栈指针选择 0选择住堆栈指针MSP 1选择进程堆栈指针 在线程模式下,可用PSP亦可用MSP 在处理者handler模式下,只允许用MSP | 线程模式下访问级别选择 0特权级的线程模式 1用户级的线程模式 |
可软中断进入异常处理,在进入异常服务程序后,LR(R14)寄存器的值被自动更新为特殊的EXC_RETURN。从EXC_RETURN, 我们可以获知硬件对哪些寄存器进行自动压栈和出栈处理。
位[31:28] | 位[27:5] | 位4 | 为3 | 位2 | 位1 | 位0 |
为0xF,作为EXC_RETURN的标识 | 保留,全为1 | 堆栈类型(硬件自动堆栈大小,单位字),M4特有 0:压入26个字 1:硬件自动向堆栈中压入8个字 在未使用FPU时,此位总为1 | 0=返回进入Handler模式 1=返回后进入线程模式 | 0=从主堆栈中做出栈操作,返回后使用MSP 1=从进程堆栈中做出栈操作,返回后使用PSP | 保留,必为0 | 0=返回ARM状 态 1=返回Thumb状态,在M3/M4中必须为1 |
举个栗子:CONTROL为0xFFFFFFE1 ----使用FPU,返回时进入Handler模式
CONTROL为0xFFFFFFE9----使用FPU,返回时进入线程模式,并用MSP
CONTROL为0xFFFFFFFD----未使用FPU,返回时进入线程模式,并用PSP
三、从操作模式和特权级别 -------操作模式 和访问特权级别。
Cortex-M3/CM4处理器支持两种处理器的操作模式,还支持两级特权操作。
两种操作模式分别为处理者模式(handler mode)和线程模式(thread mode)。引入两个模式本意是为了区别普通应用程序的代码和异常服务例程的代码----即中断服务例程的代码。
线程模式----普通应用程序代码
处理者模式(handle mode)---异常服务例程的代码
Cotex-M3/M4的特权的分级----特权级和用户级。这提供了一种存储器访问的保护机制(使得普通的用户程序代码不能意外地,甚至恶意地执行涉及到要害的操作)。处理器支持两种特权级
特权级下,程序可以访问所有范围的存储器(如果有MPU,还要在MPU规定的禁地之外),并且可以执行所有指令。
在CM3/CM4运行主应用程序时(线程模式),既可以使用特权级,也可以使用用户级;
但是异常服务例程必须在特权级下执行。
复位后,处理器默认进入线程模式,特权级访问。 但一旦切换至用户级,在想回到特权模式,就需要走"法律程序"——它必须先“申诉”----执行一条系统调用指令(SVC)---从而触发SVC异常,然后由异常服务例程(这通常是操作系统的一部分)接管,如果批准了进入,则异常服务例程会修改CONTROL寄存器,如此才能在用户级的线程模式下重新进入特权级。
从用户级到特权级的唯一途径就是异常。
复位进入-->特权级访问,线程操作模式--->修改了CONTROL寄存器---->用户级访问,线程操作模式----触发异常-->特权级访问handler操作模式--->异常返回--->特权级访问,线程操作模式/用户级访问,线程操作模式。
特权级和用户级访问级别,就能够在硬件水平上限制某些不受信任的或者还没有调试好的程序,不让它们随便地配置涉及要害的寄存器。
配了MPU(存储器保护单元)后,它还可以作为特权机制的补充——保护关键的存储区域(操作系统的区域)不被破坏。
四、Cortex-M3/M4堆栈 R13(SP)
Cortex-M3/M4 使用的是"向下生长的满栈"模型
满栈——即堆栈指针SP指向最后一个被压入堆栈的32为数值。
向下生长——在下一次压栈时,SP先自减4,在存入新的数值。
PUSH{R0}——压栈操作
POP{R0}——出栈操作:先从SP指针处读出上一次被压入的值,再把SP指针自增4。
5、中断和异常
6、存储器映射
Cortex-M3/M4 是32位处理器,可访问4GB的存储空间,它可被划分为若干个区域。
Cortex-M3预先定义好了"粗线条的"存储器映射。
通过把片上外设的寄存器映射到外设区(0x40000000~0x5FFFFFFF) 512MB,就可以简单地以访问内存的方式来访问这些外设的寄存器,从而控制外设的工作。因此,片上外设可使用C语言来操作。
预定义的映射关系,也可使得访问速度做到高度的优化,集成性更好。
从低到高 代码区--->片上SRAM--->片上外设---->片外RAM--->片外外设---->系统级存储器
片外RAM、片外外设为1G大小的映射区,其他为512MB大小的映射区
处于最高地址映射区为系统级存储区,是CM3用于藏"私房钱"的------包括中断控制器、MPU以及各种调试组件。
所有这些设备均适用固定的地址,通过把基础设施的地址定死,就至少在内核水平上,为应用程序的移植扫清障碍。
存储器保护单元(Memory Protected Uint,MPU)
Cortex-M3有一个可选的存储器保护单元。对MPU进行配置时,就可以对特权级访问和用户级访问施加不同的访问限制。
当检测到violated(犯规)时,MPU就会长生一个fault(异常),可由fault异常的服务例程来分析该错误,并且在可能时改正它。
MPU玩法很多:
最常见:操作系统使用MPU,可使得特权级代码的数据(包括操作系统本身的数据不被其他程序弄坏)。
MPU在保护内存时是按区(region)管理的,即将某些内存region设置成只读....。
7、总线接口
Cortex-M3内部有若干个总线接口,以使CM3能同时取址和访内(访问内存),它们是:
指令存储区总线(两条), 分别为I-Code总线和D-Code总线。 I-Code总线用于取指, D-Code用于查表等操作,它们按最佳执行速度进行优化。
系统总线,用于访问内存和外设,可访问的区域包括SRAM,片内外设,片外RAM,片外扩张设备以及系统级存储区的部分空间。
私有外设总线——负责一部分私有外设的访问,主要就是访问调试组件,也是在系统级存储区。
以芯片stm32f103的系统架构来看
STM32f103主系统由4个驱动单元和四个被动单元
内核: DCode-bus、 ICode-bus System-bus; 通用DMA总线
内部SRAM 内部Flash AHB到APB的桥 FSMC
1、ICode总线:该总线将M3内核指令总线和闪存指令接口相连,指令的预取在这总线上完成。1---flash接口-->7
2、DCode总线:该总线将M3内核的DCode总线和闪存存储器的数据接口相连接,常量加载和调试访问在该总线上面完成。
3、系统总线:该总线连接M3内核的系统总线到总线矩阵,总线矩阵协调内核和DMA间访问。2---->总线矩阵--->Flash接口
4、DMA总线:该总线将DMA的AHB主控接口与总线矩阵相连,总线矩阵协调CPU的DCode和DMA到SRAM、闪存和外设的访问。2--->总线矩阵--->6/8/9 4--->总线矩阵---->6/8/9
5、总线矩阵:总线矩阵解调内核系统总线和DMA主控总线之间的访问仲裁。仲裁利用轮换算法
6、AHB/APB桥