Cortex-M3重启流程——笔记

个人学习的笔记,希望帮助的和我一样阅读Cortex-M3权威指南Cn遇到困难的人。
Cortex-M3重启流程------笔记



在cortex m3 中,启动与重映

在Cortex-M3中,启动重映射是一套紧密配合的机制,它决定了处理器上电复位后第一条指令从哪里获取,以及系统如何看待内存的布局。

第一部分:启动
首先Cortex-M3内核定义了一个固定的硬件启动流程,所有基于该内核的芯片都必须遵守。
核心启动流程处理器释放复位后,硬件自动执行以下操作,无需任何软件指令:

  1. 读取初始主堆栈指针(MSP)
    • 从地址 0x0000 0000 读取第一个32位值。
    • 将这个值加载到 主堆栈指针(MSP,R13) 寄存器中。这是系统在特权模式下使用的堆栈,为即将开始的C代码和异常处理提供栈空间。
  2. 读取复位向量
    • 从地址 0x0000 0004 读取第二个32位值。
    • 这个值就是复位向量 ,即 Reset_Handler 函数的入口地址。
    • 硬件将这个地址加载到程序计数器(PC,R15) 寄存器中。
  3. 开始执行
    • 处理器跳转到PC指向的地址,正式开始执行 Reset_Handler 函数内的代码。
      这个位于 0x00000000 的数据结构被称为 "向量表" 。它不仅仅包含MSP和复位向量,后续地址还依次排列着所有异常(如NMI、硬错误)和中断(IRQ)的处理函数入口地址。
      关键概念:向量表
      向量表是连接硬件异常与软件处理程序的桥梁。其前几个条目是固定的:
地址 内容 备注
0x0000 0000 初始MSP值 主堆栈的起始地址
0x0000 0004 复位向量 Reset_Handler 的地址
0x0000 0008 NMI处理函数地址
0x0000 000C 硬错误处理函数地址
0x0000 0010 内存管理错误处理函数地址
... ... 后续为其他异常和中断向量
这一切的前提是 :硬件从 0x00000000 开始取数据。那么,这个地址到底对应芯片里的哪块物理存储器呢?这就是重映射要解决的问题。

第二部分:重映射

重映射是指在不改变CPU指令的情况下,通过硬件配置,将CPU对某段逻辑地址的访问,定向到不同的物理存储器上 。对于启动来说,最关键的就是重映射 0x00000000 开始的这块地址。
在Cortex-M3中,重映射主要通过两种方式实现,它们作用在不同的层级。
一:芯片级静态重映射(通过BOOT引脚)

这是由芯片制造商(如ST、NXP)实现的硬件特性。以STM32F1系列为例,它通过BOOT0BOOT1引脚的电平,在复位时选择将哪一块物理存储器 映射到0x00000000这个逻辑起始地址。

c 复制代码
// 示例:STM32F103的启动模式选择
// BOOT1=0, BOOT0=0: 从主Flash启动 (用户程序)
//     -> 物理Flash (0x08000000) 被映射到 0x00000000
//     硬件访问0x00000000,实际读取的是0x08000000的内容。

// BOOT1=0, BOOT0=1: 从系统存储器启动 (内置Bootloader)
//     -> 系统Bootloader ROM (0x1FFF0000) 被映射到 0x00000000
//     用于通过串口/USB下载程序。

// BOOT1=1, BOOT0=1: 从内置SRAM启动
//     -> SRAM (0x20000000) 被映射到 0x00000000
//     用于调试或运行临时性代码。

这个过程发生在CPU执行第一条指令之前,由芯片内部的硬件总线矩阵完成。它是一种静态的、由硬件引脚决定的"一次性"映射。

二:内核级动态重映射(通过VTOR寄存器)

这是Cortex-M3内核提供的软件可配置 的重映射功能,更加灵活。它通过一个名为 VTOR(向量表偏移寄存器) 的系统控制寄存器来实现。

  • VTOR地址0xE000ED08 (属于内核的SCB区域)
  • 功能 : VTOR的值定义了向量表的新基地址 。重置后,它的值通常是0x00000000
    工作原理
    当VTOR被设置为一个新地址(如 0x08010000)后,内核硬件会自动进行以下计算: 异常向量地址 = VTOR + 异常编号 × 4
    例如,当发生中断10时:
  • 旧方式(VTOR=0):硬件去 0x00000028 (0 + 40) 取向量。
  • 新方式(VTOR=0x08010000):硬件去 0x08010028 (0x08010000 + 40) 取向量。
    这是真正的"重映射":你通过修改VTOR,告诉CPU:"请到新的地址去寻找向量表"。CPU之后所有对异常向量的查找,都会基于这个新基址。

第三部分:启动与重映射的配合流程

一个典型系统的完整启动旅程是这样的,我们以从主Flash启动 并运行一个包含Bootloader应用程序的系统为例:

  1. 第一阶段:芯片硬件映射
    • 系统上电,BOOT引脚设置为从主Flash启动。
    • 芯片内部总线将物理地址 0x08000000(Flash起始)硬件映射 到逻辑地址 0x00000000
  2. 第二阶段:内核固定读取
    • Cortex-M3内核从 0x00000000 读取MSP,从 0x00000004 读取PC。
    • 由于硬件映射,它实际读取的是 0x080000000x08000004 的内容。
    • 内核跳转到 Reset_Handler(假设是Bootloader的入口)。
  3. 第三阶段:Bootloader运行与重映射
    • Bootloader开始执行。它可能检查是否有新固件,或直接跳转到应用程序。
    • 在跳转前,Bootloader会计算应用程序向量表的地址 (例如,应用程序存放在Flash的 0x08010000 处)。
    • Bootloader将VTOR寄存器的值设置为 0x08010000。这个操作瞬间完成了内核级的重映射。
    • 然后,Bootloader使用一个"跳转到应用程序"的技巧:它模拟一次异常返回,将应用程序的MSP(从新的向量表 0x08010000 处读取)和复位向量(从 0x08010004 处读取)加载,从而无缝地将控制权、堆栈和向量表全部移交给应用程序。
  4. 第四阶段:应用程序运行
    • 应用程序的代码开始运行。
    • 此后发生的任何中断,CPU都会根据新的VTOR(指向应用程序的向量表)去寻找正确的中断服务程序。

注意:

  1. Bootloader设计:这是重映射最经典的应用。Bootloader和应用程序可以有各自独立、位置不同的向量表,通过VTOR切换实现干净利落的跳转。
  2. 固件升级 :新固件可以下载到Flash的另一区域(如 0x08020000),升级时只需让Bootloader将VTOR指向新地址即可,实现"无感"切换。
  3. 在RAM中调试 :将程序加载到RAM(0x20000000),并将VTOR指向RAM,可以极快地下载和调试代码,无需擦写Flash。
  4. 高级操作系统 :RTOS在进行上下文切换时,可能会为不同任务设置不同的堆栈。通过临时修改MSP和VTOR(如果需要),可以实现更复杂的任务隔离。
    总结
  • 芯片级静态重映射(BOOT引脚)解决了"从哪里开始"的问题,提供了固件恢复和下载的物理手段。
  • 内核级动态重映射(VTOR寄存器)解决了"如何灵活切换"的问题,为现代嵌入式软件(尤其是Bootloader和RTOS)提供了至关重要的灵活性。

在芯片级静态重映射时可以通过BOOT0BOOT1引脚配置三种不同的启动方式,接下来我们将逐一讲解他们的在cortex m3中的流程

在讲解流程前,先明确三位"主角":

  1. 主堆栈指针MSP (Main Stack Pointer) ,即 R13 寄存器在特权模式下的主要形态。它指向主堆栈的栈顶,用于操作系统内核、异常及中断处理。
  2. 程序计数器PC (Program Counter) ,即 R15 寄存器。它存储着下一条要执行的指令的地址,是控制程序流程的绝对核心。
  3. 向量表偏移寄存器VTOR (Vector Table Offset Register) ,一个位于系统控制块(SCB)中的内存映射寄存器(地址 0xE000ED08)。它存储着向量表的基地址,是动态重映射的指挥棒。

主Flash启动(应用程序标准模式)

这是芯片出厂后运行用户程序的正常模式。

  1. 复位瞬间(硬件映射)
    • 操作 :芯片的引脚配置电路总线矩阵 根据 BOOT0=0 的电平,建立一条物理连接:将 CPU 对逻辑地址 0x00000000 的访问,重定向 到物理 Flash 的起始地址 0x08000000
    • 作用 :这是一个地址转换开关。它欺骗了CPU ,让CPU以为向量表在0地址,但实际上访问的是Flash。此阶段没有修改任何CPU寄存器,只是改变了内存访问的路径。
  2. 内核启动序列(硬件自动加载SP和PC)
    • 操作
      a. CPU内核从 0x00000000 读取4字节 -> 由于硬件映射,实际从 0x08000000 读取 -> 将该值直接装入MSP (R13)
      b. CPU内核从 0x00000004 读取4字节 -> 由于硬件映射,实际从 0x08000004 读取 -> 将该值直接装入PC (R15)
    • 作用
      • 设置MSP :为后续C语言函数调用(需要栈帧)和即将到来的任何异常(异常处理第一件事就是压栈)提供了合法的堆栈空间。没有这一步,系统无法运行任何高级代码或响应中断。
      • 设置PC :强制CPU跳转到用户程序的 Reset_Handler 函数。这是软件世界开始的唯一入口
  3. 软件初始化与重映射(可选,由软件操作)
    • 操作 :在 Reset_Handler 或后续初始化代码中,通过 LDRSTR 指令,将一个新的基地址 (如 0x08020000写入VTOR寄存器0xE000ED08)。
    • 作用 :写入VTOR后,CPU内部用于计算异常向量的基址指针 发生了改变。此后,当发生中断#n时,硬件不再去 0x00000000 + n*4 找向量,而是去 VTOR + n*4 找。这使得Bootloader(在 0x08000000)和应用程序(在 0x08020000)可以拥有各自独立的向量表,实现干净切换。

补充:

  1. CPU硬件动作 :复位后,CPU固定地 去逻辑地址0x00000004(通过BOOT映射,实际为物理地址0x08000004)读取一个32位数。
  2. 读取到指针 :它从0x08000004读到的不是指令 ,而是一个数据,比如0x080000C1。这个数据就是"指针"。
  3. 跳转 :CPU将这个读取到的值(0x080000C1)加载到程序计数器,然后跳转到那个地址0x080000C1)去执行。
  4. 执行函数 :地址0x080000C1才是Reset_Handler函数第一条指令的实际位置,CPU从这里开始执行该函数的代码。

系统存储器启动(内置Bootloader模式)

此模式用于通过串口等接口烧录用户程序。

  1. 复位瞬间(硬件映射)
    • 操作BOOT0=1 触发总线矩阵,将CPU对 0x00000000 的访问,重定向到芯片内部只读的系统存储器 (地址如 0x1FFF0000),这里固化着厂商提供的Bootloader代码。
    • 作用:让CPU的"第一眼"看到的是厂商Bootloader,而不是用户可能已经损坏的Flash程序。
  2. 内核启动序列(硬件自动加载SP和PC)
    • 操作 :与上述过程完全一致,但数据来源变成了系统存储器的 0x1FFF00000x1FFF0004
    • 作用 :MSP被设置为Bootloader所需的栈顶;PC跳转到Bootloader的 Reset_Handler,开始执行烧录逻辑。
  3. 内置Bootloader的工作与切换
    • 操作 :Bootloader运行,通过串口接收新程序数据,并写入用户Flash区域(如 0x08000000)。完成后,它需要将控制权交给新程序。
    • 关键切换操作 :Bootloader不会 去写VTOR(因为新程序在0地址)。它需要模拟一次"新的启动":
      a. 读取用户程序的栈指针 :从 0x08000000 读取一个字,这个值就是新程序的初始MSP。
      b. 读取用户程序的入口地址 :从 0x08000004 读取一个字,这就是新程序的 Reset_Handler 地址。
      c. 执行跳转 :通常使用一条 BX 指令,或者更常见的是,将入口地址加载到某个寄存器(如R0),然后使用 MSR 设置MSP,最后 BX R0此操作直接修改了MSP和PC
    • 作用 :通过软件手动重复了内核的启动加载序列,实现了从Bootloader到用户程序的上下文切换 。此时,向量表自然地位于 0x08000000(即VTOR默认值0所指向的位置)。

嵌入式SRAM启动(调试模式)

此模式用于在RAM中快速调试代码,无需编程Flash。

  1. 复位瞬间(硬件映射)
    • 操作BOOT0=1, BOOT1=1 触发总线矩阵,将CPU对 0x00000000 的访问,重定向到内部 SRAM的起始地址 0x20000000
    • 作用:让CPU从SRAM启动。但此时SRAM是随机或无意义的数据。
  2. 内核启动序列(硬件自动加载SP和PC)------ 但依赖调试器
    • 前提操作 :在CPU复位 、执行调试器(通过SWD/JTAG接口) 必须将编译好的程序(其链接地址被设定在SRAM空间,如 0x20000000下载到SRAM中 。这包括在 0x20000000 处放置正确的初始MSP值,在 0x20000004 处放置 Reset_Handler 地址。
    • 操作 :CPU随后执行标准加载:从 0x20000000 加载MSP,从 0x20000004 加载PC。
    • 作用 :MSP被设置为SRAM中预置的栈顶;PC跳转到也已存放在SRAM中的 Reset_Handler 代码。整个过程完全在RAM中进行,速度极快。
  3. 软件运行
    • 操作:程序在SRAM中正常运行。VTOR通常保持为0,因为向量表(在SRAM中)已经被映射到了0地址。
    • 作用 :所有代码执行、变量存取、中断响应都发生在SRAM中,实现了零等待、可反复瞬时下载的调试体验。

从这些方法我们可以看出Cortex-M3所有启动方法,本质上是硬件为软件搭建舞台并强行移交控制权的一个确定、不可更改的三层递进过程。无论从哪种存储器启动,其核心都遵循以下通用逻辑:

第一阶段:物理映射层 ------ 决定"数据之源"

在CPU内核行动之前,由芯片厂商的硬件 (通过BOOT引脚配置)完成。此阶段的核心操作是地址重定向 :芯片内部的存储器控制器将CPU内核要访问的逻辑起始地址 0x00000000,静态地关联到某一块物理存储器的基地址 (如主Flash的0x08000000、系统ROM的0x1FFF0000或SRAM的0x20000000)。
作用:此操作在物理层面决定了CPU"第一眼"看到的数据来自何处,但它不改变CPU内部的任何寄存器,只是为后续的读取铺设了一条固定的数据通路。这是启动模式差异性的唯一来源。

第二阶段:内核强制加载层 ------ 建立"运行基石"

CPU释放复位后,Cortex-M3内核硬件严格按照架构定义,自动执行以下不可打断的序列:

  1. 加载主堆栈指针 :从地址 0x00000000 读取4字节数据,直接写入MSP寄存器。由于第一阶段的重定向,此数据实际来自被映射的物理存储器。
  2. 加载程序计数器 :从地址 0x00000004 读取4字节数据,直接写入PC寄存器 。同样,该数据实际来自映射的物理存储器。
    作用 :这两个强制加载动作是系统启动的绝对核心。MSP的建立 为即将执行的C代码和任何可能立即发生的异常提供了至关重要的栈内存空间,这是函数调用和局部变量得以存在的基础。PC的加载 则粗暴地夺过了程序执行流,将其跳转到预设的入口函数(Reset_Handler),从而将控制权从硬件逻辑移交给软件指令。此阶段过后,系统已具备运行高级代码的基本条件。

第三阶段:软件初始化与重定向层 ------ 实现"动态扩展"

控制权移交至 Reset_Handler 后,开发者软件开始主导。此阶段的首要操作通常是初始化系统时钟、内存等。而其最核心的灵活性体现在:

  • 重定向向量表 :通过向VTOR寄存器 写入一个新的基地址,软件可以动态地将整个异常向量表重定位到内存中的任何位置。
    作用 :VTOR操作改变了CPU内核内部用于查找异常处理程序的基准指针。这实现了运行时层面上的"重映射",使得Bootloader与应用程序、不同任务或固件版本可以拥有各自独立的异常管理系统。这是构建复杂、可升级嵌入式系统的关键,它赋予了软件在硬件设定的固定起点之上,定义未来运行时规则的强大能力。

总结: Cortex-M3的共同启动流程可精炼为:"硬件固定映射源头 → 内核强制加载栈与入口 → 软件自由重定规则"



在cortex m3中,复位信号

在Cortex-M3系统中,复位是让整个芯片从确定状态重新开始的根本机制。主要分为由芯片厂商实现的物理复位源由ARM内核定义与响应的复位类型

主要的复位信号及其作用

根据Cortex-M3技术参考手册,从内核视角看,复位主要分为三类,它们的作用域和严格程度不同。
上电复位

  • 信号/源头 :通常由芯片的电源管理电路 或外部复位引脚 (如nRST)产生。这是最彻底、最根本的复位。
  • 作用 :将芯片所有数字逻辑和模拟电路(如PLL、稳压器)恢复到确定的初始状态。它发生在芯片刚通电或电源电压跌落到安全阈值以下时。
  • 硬件影响 :所有寄存器、存储单元、状态机都被清零或置为初始值。
    系统复位
  • 信号/源头
    • 外部复位引脚nRST)。
    • 看门狗定时器超时(独立看门狗或窗口看门狗)。
    • 电源检测(如欠压复位BOR)。
    • 软件请求 (通过置位 AIRCR.SYSRESETREQ 寄存器位)。
  • 作用 :复位处理器内核几乎所有的片上外设 (除了一些特定的"备份域"外设,如RTC、备份寄存器)。它 影响处理器的调试逻辑,使得调试器在系统复位后仍能保持连接和控制。
  • 硬件影响 :MSP、PC、通用寄存器、系统控制寄存器(SCB)以及所有外设寄存器(除备份域)被重置。
    处理器内核复位
  • 信号/源头 :通常由调试系统(通过JTAG/SWD接口)发起,例如在调试器中点击"CPU Reset"。
  • 作用仅复位Cortex-M3处理器内核 (包括寄存器、流水线、内部状态机),而完全不触动 芯片上的存储器系统外设
  • 硬件影响 :PC、MSP、通用寄存器、内核状态寄存器(如CONTROL, PRIMASK)被重置,但SRAM中的数据、Flash内容、外设的配置和状态全部保持原样 。这是调试时非常有用的功能,可以只让CPU重新开始执行,而不破坏当前的程序运行环境。
    三者关系总结上电复位 ⊃ 系统复位 ⊃ 处理器内核复位。上电复位范围最广,处理器内核复位范围最窄、最精确。

硬件上的详细操作流程(以系统复位为例)

下面描述一次典型的系统复位(如按下外部复位按钮)在硬件层面引发的连锁反应。
阶段一:复位信号的产生与传播(由芯片物理逻辑完成)

  1. 触发 :用户按下复位按钮,导致 nRST 引脚被拉低。
  2. 同步与消抖 :芯片内部的复位发生电路检测到这个低电平。该电路通常会进行消抖 (过滤毛刺)和时钟同步(将异步的复位信号同步到芯片的主时钟域),防止亚稳态。
  3. 广播 :经过处理的复位有效信号成为一个全局的 SYSRESETn 信号,被同时广播 到芯片的各个模块:
    • Cortex-M3 处理器内核
    • 所有数字外设(GPIO, UART, TIM等)
    • 存储器控制器(Flash, SRAM接口)
    • 总线矩阵 (AHB, APB)
      阶段二:处理器内核的响应(Cortex-M3内核的固定行为)
      SYSRESETn 信号有效(低电平)时,内核立即停止当前所有操作,进入复位状态。
  4. 冻结执行
    • 立即终止正在流水线中执行的指令。
    • 清空整个指令流水线。
    • 停止预取指单元和所有总线活动。
  5. 重置内部状态
    • 将所有内部状态机(如中断序列器、总线接口状态机)强制复位到初始状态。
    • 清除写缓冲中所有未完成的数据。
    • (如果存在)将MPUNVIC等单元恢复到禁用或默认状态。
    • 调试组件置于非侵入状态(允许调试器连接)。
  6. 初始化关键寄存器
    • 程序计数器被置为一个未定义值(硬件内部状态)。
    • 堆栈指针通用寄存器 等未被初始化,保持随机值,直到下一阶段被软件加载。
      阶段三:复位信号的释放与系统重启
      当外部复位按钮被释放,nRST 引脚变高后,芯片开始启动。
  7. 同步释放:芯片内部的复位发生电路同样将释放信号同步到主时钟,确保所有模块在同一时钟边沿脱离复位状态。
  8. 等待时钟稳定:如果复位释放与系统主时钟(如外部晶振)的启动同时发生,硬件会等待时钟稳定(通常经过若干振荡周期),确保逻辑在可靠的时钟下启动。
  9. 内核启动序列 :这是Cortex-M3内核强制性、不可更改 的硬件操作:
    • 第一步 :内核从地址 0x00000000 读取第一个字 (4字节),并将其作为初始主堆栈指针 值加载到 MSP (R13) 寄存器中。此时,该地址的实际物理位置由芯片的BOOT引脚配置决定(映射到主Flash、系统存储器或SRAM)。
    • 第二步 :内核从地址 0x00000004 读取第二个字 ,这个值就是复位向量 ------Reset_Handler函数的入口地址。内核将这个地址加载到 PC (R15) 寄存器中。
  10. 外设与存储器的启动:与此同时,总线矩阵、存储器控制器和各外设在脱离复位后,也进行各自的默认初始化。Flash控制器可能进入读取状态,SRAM变得可访问,所有外设寄存器恢复为复位默认值(通常为0x00000000)。
  11. 软件接管 :PC跳转到 Reset_Handler软件的第一条指令开始执行 。软件随后会:
    • 初始化系统时钟。
    • .data 段从Flash复制到SRAM(变量初始化)。
    • .bss 段清零(未初始化全局变量清零)。
    • 设置向量表偏移寄存器(VTOR)。
    • 最终调用 main() 函数,应用程序正式开始运行。

以下是Cortex-M3权威指南Cn原文:

基于CM3的单片机对复位电路有特定的要求,具体内容在《Cortex‐M3 Technical Reference Manual(Ref1)》中给出,它列出了若干个可以使用的复位信号。不过,实现成单片机后,往往只用到了1至2个。至余其它的,芯片厂商会在芯片中布设复位信号发生器,由它在内部产生剩余的复位信号。细节需要参考制造商提供的数据手册,以获取如何正确复位其芯片的信息。



在cortex m3 中,VTOR的重映射与BOOT引脚的重映射

可以用一个精准的比喻来定位:

  • BOOT引脚重映射 ,解决的是 "物理位置" 问题。即:CPU的引脚发出的电信号,最终到达哪一块物理芯片的引脚。
  • VTOR重映射 ,解决的是 "逻辑寻址" 问题。即:CPU内部执行指令时,用于计算地址的那个基准数值是什么。

下面从四个核心维度进行剖析:
1. 操作主体与控制层级

  • BOOT引脚重映射 :这是芯片制造商 (如ST、NXP)在其硅片设计中实现的物理电路功能 。它位于处理器内核之外,属于"芯片级"或"板级"硬件。用户通过设置引脚电平来"触发"这个预设功能,但无法改变其电路逻辑。
  • VTOR重映射 :这是处理器架构 (ARM Cortex-M3)定义并提供的一个软件可编程寄存器 。它位于处理器内核之内 ,属于"内核架构级"特性。开发者通过编写SCB->VTOR = xxxx这样的指令来"命令"内核改变其内部行为。
    2. 作用时机与可变性
  • BOOT引脚重映射 :其作用发生在芯片上电或硬复位过程中 ,在CPU内核开始取指之前 就已经完成。一旦复位结束,在整个芯片运行周期内,这个映射关系固定不变,除非再次发生硬件复位并改变引脚电平。
  • VTOR重映射 :其作用发生在CPU已经开始执行软件指令之后 。你可以在系统启动时、运行中、甚至中断服务程序里随时修改它。修改后,其效果立即生效 ,且可以被再次修改
    3. 作用范围与影响对象
  • BOOT引脚重映射 :它影响CPU对整个逻辑地址空间最开头一段区域 (通常是整个代码区或SRAM区)的所有访问请求,无论这个请求是取指令、读数据还是写数据。它像一个透明的地址过滤器,全局生效。
  • VTOR重映射 :它只且仅 影响CPU内核中异常处理单元 在进行"查找异常处理函数地址"这一特定操作时所使用的基地址 。它对普通的LDRSTR等数据访问指令毫无影响
    4. 根本目的与设计哲学
  • BOOT引脚重映射 :其根本目的是提供硬件级的启动容错和模式选择 。它让一块物理芯片能灵活应对不同场景:正常模式(从用户Flash启动)、救援模式(从内置Bootloader启动)、调试模式(从SRAM启动)。这是芯片厂商提供给用户的硬件工具箱
  • VTOR重映射 :其根本目的是实现软件层的模块化解耦与动态调度 。它让不同的软件模块(如Bootloader和App)能拥有各自完全独立、互不干扰的中断管理系统,并能实现运行时的无缝切换。这是处理器架构赋予软件的系统控制权

核心关联:它们如何协同工作

理解它们并非竞争关系,而是接力关系,至关重要。以最常见的"从Flash启动并运行Bootloader+App"为例:

  1. 复位瞬间BOOT0=0的硬件电路,将CPU对"0地址"的访问物理连接 到Flash芯片(0x08000000)。这是硬件确保了起点正确
  2. 内核启动 :CPU以默认VTOR=0去取向量,由于步骤1,实际从Flash取得正确值并跳转。硬件映射引导了软件的初次执行
  3. 软件初始化 :Bootloader代码执行SCB->VTOR = 0x08000000。这标志着软件开始接管,并宣布:"从此以这个真实物理地址为基准,不再依赖那个硬件把戏。" 这是软件建立确定性的第一步。
  4. 软件跳转 :Bootloader在跳转到App前,执行SCB->VTOR = 0x08002000。这完成了纯软件层面的、精细的控制权移交。此时,BOOT引脚的映射早已完成其历史使命,不再参与。


在cortex m3 中,复位与启动与重映

在cortex m3 中复位也算是一种特殊的中断(异常)(复位的异常编号 1 )。内核启动的硬件行为,本质上就是对第一个、也是最高优先级异常的响应流程

Cortex-M3的异常处理模型是统一的:发生异常 -> 硬件自动根据异常编号查找向量表 -> 跳转到处理函数。复位完全遵循此模型,但其特殊性在于:

  1. 触发条件与上下文不同
    • 普通异常:发生在CPU运行时 ,硬件会自动压栈当前上下文(PC, PSR, 寄存器等),再跳转。
    • 复位异常:发生在CPU上电或复位 时,没有需要保存的上下文(系统无状态)。硬件响应后,不压栈,直接跳转。
  2. 向量表条目固定 :在向量表中,第一个条目(偏移0x0)是MSP初值第二个条目(偏移0x4)就是复位异常(异常1)的处理函数地址 ,即 Reset_Handler。这与其他异常(如中断号0对应偏移0x40)的定位方式完全相同:异常入口地址 = 向量表基地址 + 异常编号 × 4
  3. 使用相同的硬件路径:无论是复位、中断还是系统调用,CPU跳转到处理函数的硬件逻辑是同一套。复位只是这个通用机制的"首次调用"。

在启动与重映射流程中的具体体现

整个启动与重映射过程,就是这套异常处理机制从"固定"到"可配置"的展现。
第一阶段:硬件强制执行的"初始异常响应"

这是复位异常最"特殊"的地方,因为它发生时,系统没有任何配置。

  1. 默认的向量表基地址 :内核硬件固定假设 当前向量表基地址 VTOR = 0x00000000 。这是复位异常响应的起点
  2. 芯片级物理映射(决定"0地址"是谁)BOOT引脚配置的硬件重映射,决定了物理上哪个存储器(主Flash、系统ROM、SRAM)占据了这个逻辑0地址 。这相当于在硬件层面,为"零状态"的CPU预选了第一张异常向量表位于哪块物理介质上。
  3. 执行固定响应序列 :CPU像响应任何异常一样,去获取处理入口:
    • 加载MSP :从 VTOR + 0x0 (即 0x00000000) 读取值(硬件访问0x00000000,实际读取的是0x08000000的内容),装入MSP。这本身就是从向量表读取数据,为后续任何异常(包括复位处理函数自身的C代码)提供栈空间。
    • 加载PC :从 VTOR + 1 × 4 (即 0x00000004) 读取值(硬件访问0x00000004,实际读取的是0x08000004的内容),装入PC。这正是读取"复位异常(编号1)的入口地址"
    • 此后,CPU开始执行 Reset_Handler,就像进入任何一个异常处理函数一样。
      这个阶段的核心体现是 :硬件用固定默认的VTOR(0)固定的异常编号(1) ,执行了一次无法被中断或抢占 的、最优先的异常响应,从而将系统从"无机"状态带入"有序"的软件世界。
      第二阶段:软件重映射(接管异常系统的控制权)
      Reset_Handler 开始运行后,软件就获得了重新配置整个异常系统的能力,这正是通过操作异常处理机制的核心------向量表偏移寄存器VTOR来实现的。
  4. 重定位向量表 :在 Reset_Handler 中,软件可以将VTOR的值修改为新的基地址(如 0x08020000)。这个操作一旦完成,就重映射了整个异常系统
    • 意义 :此后,所有异常 (包括复位、NMI、硬错误以及所有外设中断)的入口地址计算,都将基于新的基址。例如,此时如果发生中断#42,硬件将自动前往 0x08030000 + 0xA8 (42*4=0xA8) 处读取处理函数地址。
  5. 在Bootloader/应用切换中的应用
    • Bootloader运行在 0x08000000,其VTOR指向自身。
    • 当Bootloader准备跳转到位于 0x08020000 的应用程序时,它做两件核心事:
      1. 设置VTOR :将VTOR改为 0x08020000,将异常系统指挥权交给应用程序。
      2. 跳转到应用复位向量 :直接读取应用程序向量表的第二个字(0x08020004),并跳转过去。这本质上是一次"手动触发"的、指向新复位异常处理函数的跳转
        这个阶段的核心体现是 :复位作为异常,其处理函数(Reset_Handler自身具备重新配置整个异常系统(包括它自己)的能力。通过修改VTOR,系统实现了从一套异常处理方案到另一套的平滑切换。

所以复位与重映射的整个过程,就是 "复位异常"的响应,从一个由硬件引脚决定的、固定的物理路径开始,逐步将控制权移交到可由软件动态配置和重定位的、统一的内存地址空间的过程

总结

阶段 行为 在统一异常模型中的解读
硬件启动 CPU从固定地址0x0和0x4加载MSP和PC 这是CPU对"复位异常(编号1)"的固定响应流程。它使用默认VTOR=0,执行了最高优先级的异常响应。
物理重映射 BOOT引脚选择哪块物理存储器响应0地址的访问 这是在硬件层面,为"零状态"CPU的默认向量表(VTOR=0)选择物理载体
软件重映射 在代码中修改VTOR寄存器的值 这是在软件层面,动态修改整个异常系统(所有异常,包括复位)的查找基址 。复位异常的处理函数 Reset_Handler 拥有这项特权。
Bootloader跳转 加载应用栈指针,跳转到应用入口 这是手动执行一次"应用复位异常响应":从新VTOR指向的向量表中,加载新MSP并跳转到新的复位处理函数。

注意:复位异常 不等于 Reset_Handler,而是: 复位异常 → (硬件自动)→ 查向量表 → (获取地址)→ 跳转执行 Reset_Handler


以下是Cortex-M3从上电复位到开始执行Reset_Handler函数的完整、详细的硬件与软件流程:

第一阶段:复位触发与硬件初始化

  1. 复位事件发生 :系统上电,或外部复位引脚(nRST)被拉低,或看门狗超时等事件产生一个全局复位信号
  2. 信号同步与广播 :芯片内部的复位生成电路对该异步信号进行消抖和同步处理,然后将其作为一个名为SYSRESETn低有效信号,同时广播至芯片所有相关模块:Cortex-M3处理器内核、所有外设、存储器控制器和总线矩阵。
  3. 模块状态冻结与重置
    • CPU内核:立即停止执行,清空流水线,终止所有进行中的总线访问,并将内部所有状态机重置为初始状态。
    • 外设与存储器:所有可配置外设的寄存器被重置为芯片手册定义的默认值(通常为0)。SRAM内容通常得以保持(除非掉电),但对其的访问被暂停。Flash存储器接口进入就绪状态。
  4. 复位信号释放 :当外部复位条件解除(如按钮释放),SYSRESETn信号被同步地置为高电平。芯片各模块在同一时钟边沿脱离复位状态,并等待系统主时钟稳定。

第二阶段:内核固定启动序列与物理地址映射

  1. 固定的逻辑起点 :Cortex-M3内核硬件设计强制规定,在释放复位后,必须从逻辑地址 0x00000000 读取初始主堆栈指针(MSP),从逻辑地址 0x00000004 读取初始程序计数器(PC)。
  2. 芯片级物理重映射(由BOOT引脚决定) :在CPU内核执行上述读取操作前,芯片制造商实现的总线矩阵 根据BOOT0BOOT1等引脚在复位时的电平,已经建立了一条固定的物理连接。它将CPU对逻辑地址0x00000000区域的访问 ,重定向到一块特定的物理存储器 的起始地址:
    • 若配置为从主Flash启动 ,则逻辑0x00000000映射到物理0x08000000
    • 若配置为从系统存储器启动 ,则逻辑0x00000000映射到物理0x1FFF0000(内置Bootloader ROM)。
    • 若配置为从内置SRAM启动 ,则逻辑0x00000000映射到物理0x20000000
    • 此映射是物理的、静态的,在本次复位周期内不会改变。它仅决定了CPU"读"和"取"操作的物理来源,不涉及对数据内容的任何解释或修改。

第三阶段:加载上下文与跳转(作为复位异常的响应)

  1. 加载初始主堆栈指针(MSP)
    • CPU内核发起对地址 0x00000000 的32位读取。
    • 通过上述硬件重映射,该请求被导向选定的物理存储器(例如 0x08000000)。
    • 从该地址读取到的4字节数据,被直接、无条件地 装入MSP(R13) 寄存器。此值由软件开发者定义,并在此物理存储器的该位置。
  2. 加载初始程序计数器(PC)------即获取复位异常向量
    • CPU内核紧接着发起对地址 0x00000004 的32位读取。
    • 同样通过硬件重映射,该请求被导向物理地址(例如 0x08000004)。
    • 从该地址读取到的4字节数据,被解释为一个内存地址,并直接、无条件地 装入PC(R15) 寄存器。
    • 在Cortex-M3异常模型中,异常编号1(复位)的向量地址计算公式为 VTOR + 1 × 4。此刻,硬件隐含的向量表偏移寄存器 VTOR值为0x00000000。因此,步骤7和8正是CPU内核以 固定VTOR=0**,对复位异常(编号1) 的固定响应流程:从VTOR+0加载MSP,从VTOR+4加载PC。**
  3. 执行跳转 :PC寄存器被更新后,处理器在下个周期开始从新的PC地址取指并执行 。这个地址,就是存储在物理存储器0x00000004(经重映射后)处的值,它指向了开发者编写并编译到该物理存储器特定位置的 Reset_Handler 函数的第一条指令

第四阶段:软件接管与系统重映射(Reset_Handler内)

  1. Reset_Handler开始执行 :CPU现在运行的是软件代码。该函数通常由芯片厂商提供的启动文件定义,用汇编或C语言编写。

  2. 初始化关键系统 :函数首先初始化系统时钟、配置Flash访问延迟,可能还会初始化少量必要的外设。

  3. 关键操作------重配置向量表偏移寄存器(VTOR)

  • Reset_Handler通过一条存储指令(例如 SCB->VTOR = 0x08000000),向地址0xE000ED08(VTOR寄存器)写入一个新的基地址。

  • 此操作具有全局和即时效应 :写入完成后,Cortex-M3内核用于计算所有异常 (包括复位、NMI、中断等)入口地址的基准点,从固定的0x00000000永久地 更改为新的地址(例如0x08000000)。

  • 意义 :这实现了软件层面的逻辑重映射 。系统从此完全脱离对"逻辑0地址"硬件映射的依赖,异常处理完全在统一的4GB地址空间中,由VTOR寄存器指向的向量表管理。这也是Bootloader能跳转到应用程序的核心机制:Bootloader在跳转前,将VTOR改为应用程序向量表的地址,从而将异常处理权移交给应用程序。

  1. 初始化运行环境Reset_Handler继续执行,将已初始化的全局变量(.data段)从Flash复制到SRAM,将未初始化的全局变量(.bss段)在SRAM中清零。

  2. 跳转到主程序 :最后,Reset_Handler调用标准的C入口函数 main() 或直接跳转到调度器,将控制权移交给用户应用程序。

流程总结

此流程是一个从绝对硬件确定性 过渡到完全软件可编程性的经典范例:

  1. 物理层 :复位信号与BOOT引脚固定地 决定了第一条指令数据的物理来源
  2. 硬件逻辑层 :内核固定地 执行从VTOR=0开始的复位异常响应序列,完成从"无机"到"有序"的强制性上下文加载
  3. 软件层 :被加载执行的Reset_Handler立即夺回控制权 ,通过重写VTOR等操作,将系统配置到一个由软件完全定义和掌控的、灵活的可运行状态。


在cortex m3中,当复位后VTOR寄存器的动作

第一步:硬件强制初始化(复位释放瞬间)

当复位信号释放,处理器内核脱离复位状态时,其硬件逻辑会执行一个不可更改的初始化操作:将VTOR寄存器的值强制设置为 0x00000000 。这是一个由Cortex-M3架构定义的、所有芯片都必须遵守的固定动作。此时,处理器异常处理单元的"基准点"被锚定在逻辑地址零。然而,对于STM32F103这类芯片,一个并行的硬件事件正在发生:芯片内部的总线矩阵根据BOOT引脚的电平,已将物理Flash的起始地址(0x08000000静态地映射 到了逻辑地址0x00000000的区域。因此,VTOR=0x00000000 这个状态在物理上等价于向量表位于 0x08000000,但处理器内核在逻辑上对此映射关系一无所知,它仅仅遵从"从VTOR(当前为0)加偏移量取地址"的规则。

第二步:内核执行首次异常响应(启动序列)

紧接着,处理器内核开始执行其固定的启动序列,这本质上是对**复位异常(编号1)**的响应:

  1. 它计算 VTOR + 0(即 0x00000000),从该地址读取4字节数据,并将其作为初始主堆栈指针(MSP)加载。
  2. 它计算 VTOR + 1*4(即 0x00000004),从该地址读取4字节数据(即复位异常向量),并将其作为程序计数器(PC)加载。
    由于第一步所述的硬件映射,这两次读取实际上均来自物理地址 0x080000000x08000004。CPU随后跳转到PC所指向的地址,即位于Flash中的 Reset_Handler 函数。

第三步:软件首次显式配置(建立确定基准)
Reset_Handler 开始执行,系统控制权从硬件转移到软件。此时,运行中的代码(例如Bootloader)必须立刻消除对硬件映射的隐含依赖,建立明确、可预测的运行时环境。因此,在 Reset_Handler 调用的早期初始化函数(如 SystemInit())中,软件会执行一条至关重要的指令:向VTOR寄存器写入一个明确的基地址,例如 0x08000000 。这一动作的深层含义是:软件正式宣告,"从现在起,向量表的基准点是物理地址 0x08000000,请忘记之前 0x00000000 的逻辑映射"。完成此操作后,处理器对所有异常的寻址计算将基于这个新的、清晰的基准点。这是软件获得对异常系统控制权的标志。

第四步详解:运行时动态重配置(如果是Bootloader+App模式)

当系统超越简单的单应用程序模式,进入需要多个独立软件模块(如Bootloader和应用程序)协作运行时,VTOR的角色就从一次性初始化寄存器,变成了一个可以在软件运行中动态改写 的系统开关。其动作遵循一个严格的"指挥权移交"协议。
阶段一:Bootloader固守其位

  • 动作 :Bootloader的Reset_Handler将VTOR设置为自身的向量表基地址(0x08000000)。
  • 作用与状态 :此动作宣告了Bootloader在运行期间对中断响应拥有绝对主权 。任何在此期间发生的中断,CPU都会严格按照 0x08000000 + 中断号×4 的公式,跳转到Bootloader代码段内对应的中断服务程序。此时,VTOR是Bootloader私有资源的管理锚点
    阶段二:跳转前夕的"政权交接"
    这是整个流程中最精妙且不容出错的一步。
  • 动作 :在Bootloader执行跳转到应用程序的最后一条指令之前 ,它必须执行一条指令:SCB->VTOR = 0x08002000
  • 微观作用 :这条存储指令的完成,在硬件上产生了一个瞬时、全局且不可逆的效果 ------处理器内部异常寻址电路的"基址指针"被原子性地从0x08000000拨到了0x08002000这不是准备,而是立刻生效
  • 为何必须此时进行(关键原因) :设想如果先跳转,再修改VTOR。在跳转完成到VTOR修改成功的这几条指令执行期间,若恰好有一个中断发生,CPU会去哪里 找中断向量?答案依然是旧的VTOR(0x08000000),即Bootloader的向量表。但此时CPU已在执行应用程序的代码,上下文完全错乱,极大概率导致硬件错误或系统死锁。因此,必须在离开当前上下文(Bootloader)之前,就为下一个上下文(应用程序)铺好所有基础设施,其中中断响应路径是最关键的一项。
    阶段三:应用程序的"主权宣告"
  • 动作 :应用程序的Reset_Handler同样会执行 SCB->VTOR = 0x08002000
  • 深层意义 :这并非冗余操作,而是软件工程确定性与鲁棒性原则 的体现。应用程序不应假设自己是由一个"正确且完好"的Bootloader启动的。它必须进行"初始化自检和确认",明确建立自己所有核心资源的掌控权。这保证了:
    1. 状态确定性:无论之前VTOR被设为何值(即使是调试器乱写的),应用程序都强制将其纠正到正确值。
    2. 代码自包含与可移植性:这段初始化代码不依赖任何外部前提,使得该应用程序可以被任何兼容的引导程序启动,甚至由调试器直接加载到RAM中运行。
    3. 逻辑清晰 :在应用程序的源码中,可以清晰地看到其向量表基地址的配置,使得维护和调试变得直观。
      补充:Bootloader跳转到应用程序的本质:Bootloader在跳转前所做的设置VTOR和加载MSP/PC 这一系列操作,就是一次对CPU硬件复位启动序列的精确软件模拟:
      在执行完 SCB->VTOR = 0x08002000 之后,Bootloader的代码立即执行以下关键操作来更新PC:
  1. 读取应用程序的"复位向量"
    Bootloader知道应用程序的向量表基址现在是0x08002000(它刚刚设置的)。它像CPU硬件在复位时做的那样,从这个基址计算出应用程序的入口地址:用入口地址 = 应用VTOR基址 + 4 = 0x08002004 。Bootloader执行一条加载指令,从地址0x08002004读取4个字节 。这个读出的值,就是应用程序Reset_Handler函数的绝对地址(假设是0x08002100)。
  2. 读取应用程序的初始栈指针(可选但推荐)
    为了保证应用程序有一个完全干净的运行环境,Bootloader通常也会从应用程序向量表的第一个条目(0x08002000)读取初始主栈指针(MSP)的值,并用它来设置当前的MSP。这确保了应用程序从它自己设计的堆栈开始运行。
  3. 执行跳转(更新PC)
    这是最后一步,也是移交控制权的物理动作 。Bootloader使用一条特殊的跳转指令,将上一步读出的应用程序Reset_Handler地址加载到PC寄存器。常用两种方法:
    • 直接加载PC :使用 LDR PC, [Rx] 指令(其中Rx寄存器存放着0x08002004读出的地址)。
    • 使用BX指令 :将地址加载到一个通用寄存器(如R0),然后执行 BX R0BX(分支并交换指令集)指令非常适合这种跳转。

总结:VTOR的动作逻辑

VTOR在复位后的动作,遵循一条清晰的逻辑主线:从硬件强制的、隐含物理映射的默认值(0x00000000),被软件显式地重定义为明确、线性的物理基地址(如 0x08000000),并可在系统运行生命周期中,根据软件逻辑的需要进行动态重定位,以支持模块化、可升级的复杂系统设计。 整个过程体现了嵌入式系统从"硬件确定的起点"平稳过渡到"软件完全掌控的灵活运行"的核心设计哲学。


补充:从复位到执行 Reset_Handler

当复位信号生效再释放,两个并行的硬件动作瞬间完成:

  1. BOOT引脚电平被锁存 :芯片根据BOOT0等引脚的电平,物理性地接通了内部一条电路 。例如BOOT0=0时,逻辑地址0x000000000x0000FFFF这片区域的访问,被硬连线 到了物理Flash存储器(0x08000000起始)的对应引脚上。这不是软件命令,是电子在硅片通道里的流向被改变了。
  2. CPU内核复位 :Cortex-M3内核的硬件状态机被清零,其向量表偏移寄存器(VTOR)被硬件强制设为0x00000000 ,程序计数器(PC)和栈指针(SP)处于无效状态。
    紧接着,内核启动其不可变更的硬件序列
  • 第一步取栈 :它"想要"从VTOR + 0(即0x00000000)读取数据作为初始主栈指针(MSP)。由于上述硬件连线,这个读请求被导向了物理地址0x08000000。于是,存储在Flash最开头的那个4字节数字(由链接脚本和编译器放置)被加载到MSP寄存器。这是软件世界对硬件栈的第一次定义。
  • 第二步取指令 :它"想要"从VTOR + 4(即0x00000004)读取数据作为复位异常向量。同样,这个请求被导向0x08000004。存储在此的另一个4字节数字(即Reset_Handler函数的入口地址)被加载到程序计数器(PC)。
  • 第一次跳转 :CPU将PC所指的地址(即Reset_Handler)作为下一条指令的地址。于是,处理器从物理Flash中Reset_Handler所在的准确位置,取回了第一条机器指令并开始执行。 至此,硬件的强制引导使命完成,控制权通过一条由物理电路决定的路径,被移交给了软件的第一行代码。
相关推荐
方见华Richard3 小时前
方见华:在递归的暗夜里,把自己活成一束光
人工智能·经验分享·笔记·学习方法·空间计算
zzcufo3 小时前
多邻国学习笔记第五阶段第10-11部分
笔记·学习·c#
BlackWolfSky3 小时前
鸿蒙中级课程笔记2—状态管理V2—@ObservedV2装饰器和@Trace装饰器:类属性变化观测
笔记·华为·harmonyos
航Hang*3 小时前
计算机等级考试(二级WPS)---第1章:综合应用基础---第2节:PDF文件应用
笔记·学习·pdf·wps·计算机二级·计算机等级考试
zhangrelay3 小时前
Linux(ubuntu)如何锁定cpu频率工作在最低能耗模式下
linux·笔记·学习
三伏5223 小时前
Cortex-M3权威指南Cn第四、五章——笔记
笔记·cortex-m3
轴测君4 小时前
MobileNet V1
人工智能·pytorch·笔记
Aliex_git4 小时前
Claude Code 使用笔记(一)- 配置和基础
人工智能·笔记·学习·ai编程
云潮汐表5 小时前
烟台潮汐表查询2026-01-25
笔记