1.寄存器
CM3拥有R0~R15通用寄存器和一些特殊功能寄存器
R0~R12这些通用寄存器,复位初始值都是不可预料的
2.CM3有R0到R15的通用寄存器组
堆栈指针R13
链接寄存器R14(LR):用于在调用子程序时,存储返回地址。使用BL指令时自动填充LR的值
程序计数器R15(PC):向PC中写数据,就会引起一次程序的分支,但不更新LR寄存器的值
注:绝大部分的16位thumb只能访问R0到R7,而32位thumb-2可以访问全部寄存器
3.特殊功能寄存器
3.1程序状态寄存器组(应用程序PSR+中断号PSR+执行PSR)
3.2中断屏蔽寄存器组:用于控制异常的除能和使能
3.3控制寄存器:用于定义特权级别和当前使用哪个堆栈指针
3.4 PRIMASK:只有一个位,当这个位置1时,就关闭所有可屏蔽的异常,只剩下NMI和硬FAULT。
FAULTMASK:只有一位,当置1时,只有NMI才能响应,所有其他的异常包括中断和fault全部关闭
BASEPRI:最多有9位,当被设置为某一值时,所有优先级号大于等于此值的中断都被关闭
4.操作模式和特权级别:
两种操作模式(处理器模式):Handler模式和线程模式(用于区分异常服务例程的代码和普通程序的代码)
两种特权等级:特权级和用户级(是指在硬件层面上对存储器访问权限的设置)
注:CM3在运行主程序(即线程模式)可以使用特权级别和用户级别;但是异常服务例程(即handler模式)只能使用特权级别。当处于线程+用户模式时一些访问权限将被禁止
将代码区分成用户级和特权级,有利于程序架构的稳定,如某一个用户代码出问题,不会使其成为害群之狗,因为用户级别的代码是禁止对一些要害寄存器操作的。
5.异常处理
5.1CONTROL[0]=0;
5.2CONTROL[0]=1;
CONTROL[0]只有在特权级别下可以访问,若在用户级别想访问先通过"系统服务呼叫指令(SVC)"来触发SVC异常,然后在该异常的服务例程中可以修改CONTROL[0]。
6.下面是各操作模式的转换
7.异常和中断
可以有11个系统异常和最多240个外部中断(IRQ),具体芯片使用了多少要看芯片制造厂商。
作为中断功能的强化,NVIC 还有一条NMI输入信号线,具体做什么由芯片制造商决定,NMI(not masked interrupted)
8.向量表:当一个异常被CM3内核接受。对应的异常Handler就会执行,向量表用来决定Handler的入口地址。
9.CM3的双堆栈:主堆栈(MSP)和进程堆栈(PSP)。是由CONTROL[1]控制的。
10.复位序列:
先从0X00地址取出MSP的值再从0x04地址取出PC的初始值,0X04处存的值是复位向量,而不是跳转指令。
此处CM3与ARM及单片机不同。以前ARM都是从0X00地址开始执行第一条指令,一般第一条指令都是跳转指令
11.MSP及PC初始化的一个例程
12.cortex-m3堆栈操作
向下生长的满栈,SP指向最后一个被压入堆栈32的寄存器数值的地址
cortexm3使用小端模式:高字节存储在高地址中,堆栈框架如图
只是将xPCR,PC,LR,R12,R3,R2,R1,R0压入堆栈
13.SVC和PendSV
SVC(系统服务调用,亦简称系统调用)和 PendSV(可悬起系统调用),它们多用在上了操作 系统的软件开发中。SVC 用于产生系统函数的调用请求。例如,操作系统通常不让用户程序直接访 问硬件,而是通过提供一些系统服务函数,让用户程序使用 SVC 发出对系统服务函数的呼叫请求, 以这种方法调用它们来间接访问硬件。因此,当用户程序想要控制特定的硬件时,它就要产生一个 SVC异常,然后操作系统提供的 SVC异常服务例程得到执行,它再调用相关的操作系统函数,后者 完成用户程序请求的服务。
这种“提出要求——得到满足”的方式,很好、很强大、很方便、很灵活、很能可持续发展。
首先,它使用户程序从控制硬件的繁文缛节中解脱出来,而是由 OS 负责控制具体的硬件。
第二, OS的代码可以经过充分的测试,从而能使系统更加健壮和可靠。
第三,它使用户程序无需在特权级 下执行,用户程序无需承担因误操作而瘫痪整个系统的风险。
第四,通过 SVC的机制,还让用户程序变得与硬件无关,因此在开发应用程序时无需了解硬件的操作细节,从而简化了开发的难度和繁 琐度,并且使应用程序跨硬件平台移植成为可能。
开发应用程序唯一需要知道的就是操作系统提供 的应用编程接口(API),并且在了解了各个请求代号和参数表后,就可以使用 SVC来提出要求了(事 实上,为使用方便,操作系统往往会提供一层封皮,以使系统调用的形式看起来和普通的函数调用 一致。各封皮函数会正确使用 SVC 指令来执行系统调用——译者注)。其实,严格地讲,操作硬件 的工作是由设备驱动程序完成的,只是对应用程序来说,它们也相当于操作系统的一部分。
SVC 异常通过执行”SVC”指令来产生。该指令需要一个立即数,充当系统调用代号。SVC 异常 服务例程稍后会提取出此代号,从而获知本次调用的具体要求,再调用相应的服务函数。例如,
SVC 0x3 ; 调用 3号系统服务
在 SVC服务例程执行后,上次执行的 SVC指令地址可以根据自动入栈的返回地址计算出。找到 了 SVC指令后,就可以读取该 SVC指令的机器码,从机器码中萃取出立即数,就获知了请求执行的 功能代号。如果用户程序使用的是 PSP,服务例程还需要先执行 MRS Rn, PSP指令来获取应用程序 的堆栈指针。通过分析 LR的值,可以获知在 SVC指令执行时,正在使用哪个堆栈(细节在第 8章 中讨论)。
PendSV(可悬起的系统调用)
另一个相关的异常是 PendSV(可悬起的系统调用),它和 SVC协同使用。一方面,SVC异常是 必须在执行 SVC指令后立即得到响应的(对于 SVC异常来说,若因优先级不比当前正处理的高,或 是其它原因使之无法立即响应,将上访成硬 fault——译者注),应用程序执行 SVC 时都是希望所 需的请求立即得到响应。另一方面,PendSV 则不同,它是可以像普通的中断一样被悬起的(不像 SVC 那样会上访)。OS 可以利用它“缓期执行”一个异常——直到其它重要的任务完成后才执行动 作。
悬起 PendSV 的方法是:手工往 NVIC的 PendSV悬起寄存器中写 1。悬起后,如果优先级不够 高,则将缓期等待执行。
PendSV的典型使用场合是在上下文切换时(在不同任务之间切换)。 PendSV编程为最低优先级的异常。