当前位置: 首页 > 工具软件 > M3O > 使用案例 >

《Cortex-M3 权威指南 笔记》

莫泓
2023-12-01

因了命途中的你们,我才没有荒芜了青春。
– 莫言

一、介绍

  • 整个 MCU 只有Cortex-M3 处理器内核(中央处理单元(CPU))和调试器是ARM设计的,而存储器,外设,I/O以及其它功能块是芯片制造厂商添加的。
  • ARM TrustZone® 技术是系统范围的安全方法,针对高性能计算平台上的大量应用,包括安全支付、数字版权管理 (DRM)、企业服务和基于 Web 的服务。(这个M3不一定有)
  • 16位的Thumb指令集在功能上是ARM指令集的一个子集,但它能带来更高的代码密度
  • Thumb-2指令集:结合16位Thumb 和 32位ARM指令集,两者之间取了一个平衡, 兼有二者的优势, 当一个 操作可以使用一条 32bits指令完成时就使用 32bits 的指令, 加快运行速度, 而当一次操作只需要一条16bits 指令完成时就使用16bits 的指令,节约存储空间。https://www.cnblogs.com/god-of-death/p/7078347.html
  • MPU 实现了部分 MMU的功能,增加了系统的安全性。

二、M3概述

  • CM3 采用了哈佛结构,拥有独立的指令总线和数据总线,可以让取指与数据访问并行不悖。指令总线和数据总线共享同一个存储器空间(一个统一的存储器系统)。换句话说,不是因为有两条总线,可寻址空间就变成 8GB 了(还是只能最高4GB)。

  • M3 一共有 R0-R15,16个寄存器。R0-R12是通用寄存器,R13-R14是特殊功能寄存器,绝大多数 16 位 Thumb 指令只能访
    问 R0‐R7,而 32 位 Thumb‐2 指令可以访问所有寄存器。

    • R13(SP)寄存器是堆栈指针,有两个(一次只能使用一个),MSP(主堆栈指针)常用于异常和操作系统内核,PSP(进程堆栈指针)用于用户程序,是复位后缺省使用的堆栈指针

      堆栈指针的最低两位永远是 0,这意味着堆栈总是 4 字节对齐的

    • R14(LR):连接寄存器,呼叫一个子程序时,由 R14 存储返回地址(RTT用来回收线程使用空间),把返回地址直接存储在寄存器中。这样足以使很多只有 1 级子程序调用的代码无需访问内存(堆栈内存),从而提高了子程序调用的效率

      如果多于一级,则压到堆栈。

      P27:在 RISC 处理器中,为了强调访内操作越过了处理器的界线,并且带来了对性能的不利影响,给它取了一个专业的术语:溅出。这个翻译看一下英文原文

    • R15(PC):程序计数寄存器
      指向当前的程序地址。如果修改它的值,就能改变程序的执行流

  • 特殊功能寄存器

    • 程序状态字寄存器组(PSRs)

      记录 ALU 标志(0 标志,进位标志,负数标志,溢出标志),执行状态,以及
      当前正服务的中断号

    • 中断屏蔽寄存器组(PRIMASK, FAULTMASK, BASEPRI):都是中断相关的

    • 控制寄存器(CONTROL): 定义特权状态,并且决定使用哪一个堆栈指针

  • 操作模式

    Cortex‐M3 处理器支持两种处理器的操作模式。处理者模式(handler mode,以后不再把 handler 中译——译注)和线程模
    式(thread mode)。引入两个模式的本意,是用于区别普通应用程序的代码和异常服务例程的代码——包括中断服务例程的代码。

  • 特权极别

    特权的分级——特权级和用户级。这可以提供一种存储器访问的保护机制,使得普通的用户程序代码不能意外地,甚至是恶意地执行涉及到要害的操作。处理器支持两种特权级,这也是一个基本的安全模型。

    中断模式一定是特权级,线程模式也有机会是特权级()

    这部分可以再仔细看看

  • M3的存储器映射:将内核相关寄存器、外设、扩展外部存储器、片上外设、片上SRAM、代码区等映射在4GB(32位)的地址空间。过把片上外设的寄存器映射到外设区,就可以简单地以访问
    内存的方式来访问这些外设的寄存器,从而控制外设的工作

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vsTFxClR-1641195339951)(笔记.assets/image-20211130172807269.png)]

  • Cortex‐M3 内部有若干个总线接口,以使 CM3 能同时取址和访内(访问内存)。采用的是哈佛结构。

  • Cortex‐M3 只使用 Thumb‐2 指令集(其实只使用了其中的一部分),该指令集可同时使用16位和32位,再也不需要花时间来切换于 32 位 ARM 状态和16 位 Thumb 状态之间了。

    (一般来说,32位更快,16位代码密度更高)

  • 低功耗(实现的逻辑门较少且支持多种节能模式)

  • 调试方便

m3基础

  • m3的堆栈使用的是“向下生长的满栈”

  • 寄存器的 PUSH 和 POP 操作永远都是 4 字节对齐的——也就是说他们的地址必须是
    0x4,0x8,0xc,……。这样一来,R13的最低两位被硬线连接到0,并且总是读出0 (Read As Zero)。

  • (待理解)如果向 PC 中写数据,就会引起一次程序的分支(但是不更新 LR 寄存器) )。CM3 中的指令至少是半字对齐的,所以 PC 的 LSB 总是读回 0。然而,在分支时,无论是直接写 PC 的值还是使用分支指令,都必须保证加载到 PC 的数值是奇数(即 LSB=1),用以表明这是在
    Thumb 状态下执行。倘若写了 0,则视为企图转入 ARM 模式,CM3 将产生一个 fault 异
    常。

  • Cortex‐M3 中的特殊功能寄存器只能被专用的 MSR 和 MRS 指令访问,而且它们也没有存储器地址

  • 有三个屏蔽中断寄存器,限制程度不一样

  • CONTROL寄存器:堆栈指针的选择、线程模式用户模式还是特权模式的选择。只有在特权模式才能修改该寄存器。

  • handler模式总是特权级的。在复位后,处理器进入线程模式+特权级。

  • 为了决定 handler 的入口地址,CM3 使用了“向量表查表机制”。这里使用一张向量表。向量表其实是一个 WORD(32 位整数)数组,每个下标对应一种异常,该下标元素的值则是该异常 handler 的入口地址。向量表的存储位置是可以设置的,通过 NVIC 中的一个重定位寄存器来指出向量表的地址。

  • push和pop的成对使用,才能有效的恢复栈中的内容

  • RTOS如何将局部变量存放在线程栈中?

    OS以修改 PSP,用于实现多任务中的任务上下文切换。

  • 在进入 ISR 时,CM3 会自动把一些寄存器压栈,这里使用的是进入 ISR 之前使用的 SP
    指针(MSP 或者是 PSP)。离开 ISR 后,只要 ISR 没有更改过 CONTROL[1],就依然使用先前
    的 SP 指针来执行出栈操作。

  • 复位序列

    从地址 0x0000,0000 处取出 MSP 的初始值。

    从地址 0x0000,0004 处取出 PC 的初始值——这个值是复位向量,LSB 必须是 1。然后从这个值所对应的地址处取指。可以将其引向启动引导代码

指令集

  • 数据传送

    • 两个寄存器间传送数据

      寄存器间传送数据的指令是 MOV(MOV R8, R3)

    • 寄存器与存储器间传送数据

      用于访问存储器的基础指令是“加载(Load)”和“存储(Store)”。加载指令 LDR 把存
      储器中的内容加载到寄存器中,存储指令 STR 则把寄存器的内容存储至存储器中,传送过程
      中数据类型也可以变通(通过在指令后面加后缀,演变出多种用法)

    • 寄存器与特殊功能寄存器间传送数据

    • 把一个立即数加载到寄存器

  • 大多数情况下,汇编器都在遇到 LDR 伪指令时,都会把它转换成一条相对于 PC 的加载
    指令,来产生需要的数据。

    对于 LDR,如果汇编器发现要产生立即数是一个程序地址,它会自动地把 LSB 置位(加载到 PC 的数值保证是奇数)。

    如果汇编器发现要加载的是数据地址,则不会自作聪明(把LSB置位)

  • 数据处理

    即加减乘除、逻辑运算(如果要用到存储器的值,先将其加载到寄存器,然后运算)

  • MRS 和 MSR(只有在特权模式下才能使用)

    MRS , ;加载特殊功能寄存器的值到 Rn

    MSR , ;存储 Rn 的值到特殊功能寄存器

#### 存储器系统

  • CM3 在定义了存储器映射之外,还为存储器的访问规定了 4 种属性,分别是:

    可否缓冲(Bufferable)
    可否缓存(Cacheable)
    可否执行(Executable)
    可否共享(Sharable)

  • 有两个区中实现了位带(支持位带操作的地址区)。其中一个是 SRAM 区的最低 1MB 范围,第二个则是片内外设区的最低 1MB 范围。这两个区中的地址除了可以像普通的 RAM 一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个 32 位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。

    位带别名:对别名地址的访问最终作用到位带区的访问上(有一个地址映射过程)

  • 在 CM3 中,非对齐的数据传送只发生在常规的数据传送指令中,如 LDR/LDRH/LDRSH。
    其它指令则不支持。(对齐能够提高程序的运行效率,所以存取数据最好是对齐的)

  • 支持大小端模式,不过一般使用小端模式。

实现Cortex-M3的整体风景

  • Cortex‐M3 处理器使用一个 3 级流水线。流水线的 3 级分别是:取指,解码和执行

  • SysTick 定时器:系统滴答定时器是一个非常基本的倒计时定时器,用于在每隔一
    定的时间产生一个中断,即使是系统在睡眠模式下也能工作。它使得 OS 在各 CM3
    器件之间的移植中不必修改系统定时器的代码,移植工作一下子容易多了。SysTick
    定时器也是作为 NVIC 的一部分实现的。

  • 嵌套向量中断控制器 NVIC:NVIC 是一个在 CM3 中内建的中断控制器。中断的具体
    路数由芯片厂商定义。NVIC 是与 CPU 紧耦合的,它还包含了若干个系统控制寄存
    器。因为 NVIC 支持中断嵌套,使得在 CM3 上处理嵌套中断时清爽而强大。它还
    采用了向量中断的机制。在中断发生时,它会自动取出对应的服务例程入口地址,
    并且直接调用,无需软件判定中断源,为缩短中断延时做出了非常重要的贡献。

  • 存储器保护单元:MPU 是一个选配的单元,有些 CM3 芯片可能没有配备此组件。
    如果有,则它可以把存储器分成一些 regions,并分别予以保护。例如,它可以让
    某些 regions 在用户级下变成只读,从而阻止了一些用户程序破坏关键数据。

  • ROM 表:它只是一个简单的查找表,提供了存储器映射信息,这些信息供包括了多种
    系统设备和调试组件

  • AHB : AHB (Advanced High-performance Bus)

  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XCxe7Hzx-1641195339954)(F:\学习资料\Cortex-M3权威指南(中文)]\笔记.assets\image-20200929105432398.png)

  • 复位信号

    • 上电复位

      在器件上电时需要把复位置为有效(assert),把处理器核心和调试系统一起复位

    • 系统复位

      只影响处理器核心、NVIC(与调试相关的除外)以及MPU,不复位调试系统

    • 测试复位

      只复位调试系统

异常

  • 编号为 1-15 的对应系统异常,大于等于 16 的则全是外部中断。除了个别异常
    的优先级被定死外,其它异常的优先级都是可编程的

  • m3对于每个异常源,在被悬起的情况下,都会有一个对应的“悬起状态寄存器”保存其异常请求,直到该异常能够执行为止,这与传统的 ARM 是完全不同的。在以前,是由产生中断的设备保持住请求信号。现在NVIC 的悬起状态寄存器的出现解决了这个问题,即使后来设备已经释放了请求信号,曾经的中断请求也不会错失。

  • 为了使抢占机能变得更可控,CM3 还把 256 级优先级(不一定有那么多,厂家会裁剪)按位分成高低两段,分别是抢占优先级和亚优先级,如下所述。

    优先级分组规定:亚优先级至少是 1 个位。因此抢占优先级最多是 7 个位,造成了最多只有 128 级抢占的现象。

  • 在计算抢占优先级和亚优先级的有效位数时,必须先求出下列值:

    • 芯片实际使用了多少位来表达优先级
    • 优先级组是如何划分的
  • 如果优先级完全相同的多个异常同时悬起,则先响应异常编号最小的那一个。如 IRQ #3
    会比 IRQ #5 先得到响应

  • 某个中断得到响应之前,其悬起状态被清除了(例如,在 PRIMASK 或 FAULTMASK 置位的时候软件清除了悬起状态标志),则中断被取消

  • 如果某个中断在得到响应之前,其请求信号以若干的脉冲的方式呈现,则被
    视为只有一次中断请求,多出的请求脉冲全部错失——这是中断请求太快,以致于超出处理
    器反应限度的情况。

  • Fault 类异常

    • 总线 Faults

      当 AHB 接口上正在传送数据时,如果回复了一个错误信号(error response),则会产生总
      线 faults.

      哪些因素会导致 AHB 回复一个错误信号?
      AHB 回复的错误信号会触发总线 fault,诱因可以是:

      • 企图访问无效的存储器 region。常见于访问的地址没有相对应的存储器。
      • 设备还没有作好传送数据的准备。比如,在尚未初始化 SDRAM 控制器的时候试图访问 SDRAM。
      • 在企图启动一次数据传送时,传送的尺寸不能为目标设备所支持。例如,某设备只接受字型数据,却试图送给它字节型数据。
      • 因为某些原因,设备不能接受数据传送。例如,某些设备只有在特权级下才允许访问,可当前却是用户级。

      发生了总线 fault 后,我们将如何找出该 fault 的事故原因呢?在这里,NVIC 提供
      了若干个 fault 状态寄存器,其中一个名为“总线 fault 状态寄存器”(BFSR)的。通过它,总
      线 fault 服务例程可以确定产生 fault 的场合:是在数据访问时,在取指时,还是在中断的堆
      栈操作时。

    • 存储器管理 faults

      存储器管理faults多与MPU有关,其诱因常常是某次访问触犯了MPU设置的保护策略

      了调查 MemManage fault 的案发现场,NVIC 中有一个“存储器管理 fault 状态寄存器
      (MFSR)”,它指出导致 MemManage fault 的原因。如果是因为一个数据访问违例(DACCVIOL位)或是一个取指访问违例(IACCVIOL 位),则违例指令的地址已经被压入栈中。如果还有MMARVALID 位被置位,则还能进一步查出引发此 fault 时访问的地址——读取 NVIC“存储器管理地址寄存器(MMAR)”的值。

    • 用法 fault

    • 硬 fault

      硬 fault 是上文讨论的总线 fault、存储器管理 fault 以及用法 fault 上访的结果。如果这
      些 fault 的服务例程无法执行,它们就会成为“硬伤”——上访(escalation)成硬 fault。

  • SVC 和 PendSV

    SVC(系统服务调用,亦简称系统调用)和 PendSV(可悬起系统调用)

    PendSV(可悬起的系统调用),它和 SVC 协同使用。一方面,SVC
    异常是必须立即得到响应的(若因优先级不比当前正处理的高,或是其它原因使之无法立即
    响应,将上访成硬 fault——译者注),应用程序执行 SVC 时都是希望所需的请求立即得到响
    应。另一方面,PendSV 则不同,它是可以像普通的中断一样被悬起的(不像 SVC 那样会上
    访)。OS 可以利用它“缓期执行”一个异常——直到其它重要的任务完成后才执行动作。悬
    起 PendSV 的方法是:手工往 NVIC 的 PendSV 悬起寄存器中写 1。悬起后,如果优先级不够
    高,则将缓期等待执行。

    PendSV 的典型使用场合是在上下文切换时(在不同任务之间切换)

    PendSV 异常会自动延迟上下文切换的请求,
    直到其它的 ISR 都完成了处理后才放行。为实现这个机制,需要把 PendSV 编程为最低优先级的异常。如果 OS 检测到某 IRQ 正在活动并且被 SysTick 抢占,它将悬起一个 PendSV 异常,
    以便缓期执行上下文切换。

NVIC(向量中断控制器)与中断控制

  • PRIMASK 用于除能在 NMI 和硬 fault 之外的所有异常,它有效地把当前优先级改为 0(可
    编程优先级中的最高优先级)

  • FAULTMASK更绝,它把当前优先级改为‐1。这么一来,连硬fault都被掩蔽了。使用方案
    与PRIMASK的相似。但要注意的是,FAULTMASK 会在异常退出时自动清零。

  • BASEPRI寄存器

    用于掩蔽优先级低于某一阈值的中断

  • SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)

中断的具体行为

  • 中断/异常的响应序列

    当相应一个中断时,会有三个操作

    • 入栈: 把8个寄存器的值压入栈

      响应异常的第一个行动,就是自动保存现场的必要部分:依次把xPSR, PC, LR, R12以及R3‐R0由硬件自动压入适当的堆栈中:如果当响应异常时,当前的代码正在使用PSP,则压入PSP,即使用线程堆栈;否则压入MSP,使用主堆栈。一旦进入了服务例程,就将一直使用主堆栈

    • 取向量:从向量表中找出对应的服务程序入口地址

    • 选择堆栈指针MSP/PSP,更新堆栈指针SP,更新连接寄存器LR,更新程序计数器PC

  • 异常返回

    启动了中断返回序列(3种途径可以触发异常返回序列)后,执行以下处理

    • 出栈:先前压入栈中的寄存器在这里恢复。内部的出栈顺序与入栈时的相对应,堆栈指
      针的值也改回去。
    • 更新NVIC寄存器:伴随着异常的返回,它的活动位也被硬件清除。对于外部中断,倘若
      中断输入再次被置为有效,悬起位也将再次置位,新一次的中断响应序列也可随之再次开始。
  • 所有服务例程都只使用主堆栈。所以当
    中断嵌套加深时,对主堆栈的压力会增大:每嵌套一级,就至少再需要8个字,即32字节的
    堆栈空间——而且这还没算上ISR对堆栈的额外需求,并且何时嵌套多少级也是不可预料的。

  • 相同的异常是不允许重入的。因为每个异常都有自己的优先级,并
    且在异常处理期间,同级或低优先级的异常是要阻塞的,因此对于同一个异常,只有在上次
    实例的服务例程执行完毕后,方可继续响应新的请求。

 类似资料: