云计算百科
云计算领域专业知识百科平台

Cortex-M3重启流程——笔记

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



在cortex m3 中,启动与重映

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

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

  • 读取初始主堆栈指针(MSP):
    • 从地址 0x0000 0000 读取第一个32位值。
    • 将这个值加载到 主堆栈指针(MSP,R13) 寄存器中。这是系统在特权模式下使用的堆栈,为即将开始的C代码和异常处理提供栈空间。
  • 读取复位向量:
    • 从地址 0x0000 0004 读取第二个32位值。
    • 这个值就是复位向量,即 Reset_Handler 函数的入口地址。
    • 硬件将这个地址加载到程序计数器(PC,R15) 寄存器中。
  • 开始执行:
    • 处理器跳转到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系列为例,它通过BOOT0和BOOT1引脚的电平,在复位时选择将哪一块物理存储器映射到0x00000000这个逻辑起始地址。

    // 示例: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和应用程序的系统为例:

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

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

    在芯片级静态重映射时可以通过BOOT0和BOOT1引脚配置三种不同的启动方式,接下来我们将逐一讲解他们的在cortex m3中的流程 在讲解流程前,先明确三位“主角”:

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

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

  • CPU硬件动作:复位后,CPU固定地去逻辑地址0x00000004(通过BOOT映射,实际为物理地址0x08000004)读取一个32位数。
  • 读取到指针:它从0x08000004读到的不是指令,而是一个数据,比如0x080000C1。这个数据就是“指针”。
  • 跳转:CPU将这个读取到的值(0x080000C1)加载到程序计数器,然后跳转到那个地址(0x080000C1)去执行。
  • 执行函数:地址0x080000C1才是Reset_Handler函数第一条指令的实际位置,CPU从这里开始执行该函数的代码。
  • 系统存储器启动(内置Bootloader模式) 此模式用于通过串口等接口烧录用户程序。

  • 复位瞬间(硬件映射):
    • 操作:BOOT0=1 触发总线矩阵,将CPU对 0x00000000 的访问,重定向到芯片内部只读的系统存储器(地址如 0x1FFF0000),这里固化着厂商提供的Bootloader代码。
    • 作用:让CPU的“第一眼”看到的是厂商Bootloader,而不是用户可能已经损坏的Flash程序。
  • 内核启动序列(硬件自动加载SP和PC):
    • 操作:与上述过程完全一致,但数据来源变成了系统存储器的 0x1FFF0000 和 0x1FFF0004。
    • 作用:MSP被设置为Bootloader所需的栈顶;PC跳转到Bootloader的 Reset_Handler,开始执行烧录逻辑。
  • 内置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。

  • 复位瞬间(硬件映射):
    • 操作:BOOT0=1, BOOT1=1 触发总线矩阵,将CPU对 0x00000000 的访问,重定向到内部 SRAM的起始地址 0x20000000。
    • 作用:让CPU从SRAM启动。但此时SRAM是随机或无意义的数据。
  • 内核启动序列(硬件自动加载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中进行,速度极快。
  • 软件运行:
    • 操作:程序在SRAM中正常运行。VTOR通常保持为0,因为向量表(在SRAM中)已经被映射到了0地址。
    • 作用:所有代码执行、变量存取、中断响应都发生在SRAM中,实现了零等待、可反复瞬时下载的调试体验。
  • 从这些方法我们可以看出Cortex-M3所有启动方法,本质上是硬件为软件搭建舞台并强行移交控制权的一个确定、不可更改的三层递进过程。无论从哪种存储器启动,其核心都遵循以下通用逻辑:

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

    第二阶段:内核强制加载层 —— 建立“运行基石” CPU释放复位后,Cortex-M3内核硬件严格按照架构定义,自动执行以下不可打断的序列:

  • 加载主堆栈指针:从地址 0x00000000 读取4字节数据,直接写入MSP寄存器。由于第一阶段的重定向,此数据实际来自被映射的物理存储器。
  • 加载程序计数器:从地址 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重新开始执行,而不破坏当前的程序运行环境。 三者关系总结:上电复位 ⊃ 系统复位 ⊃ 处理器内核复位。上电复位范围最广,处理器内核复位范围最窄、最精确。

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

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

    ![[典型的Cortex-M3芯片内部复位信号和其作用范围示意图.png]]



    在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内核中异常处理单元在进行“查找异常处理函数地址”这一特定操作时所使用的基地址。它对普通的LDR、STR等数据访问指令毫无影响。 4. 根本目的与设计哲学
    • BOOT引脚重映射:其根本目的是提供硬件级的启动容错和模式选择。它让一块物理芯片能灵活应对不同场景:正常模式(从用户Flash启动)、救援模式(从内置Bootloader启动)、调试模式(从SRAM启动)。这是芯片厂商提供给用户的硬件工具箱。
    • VTOR重映射:其根本目的是实现软件层的模块化解耦与动态调度。它让不同的软件模块(如Bootloader和App)能拥有各自完全独立、互不干扰的中断管理系统,并能实现运行时的无缝切换。这是处理器架构赋予软件的系统控制权。

    核心关联:它们如何协同工作 理解它们并非竞争关系,而是接力关系,至关重要。以最常见的“从Flash启动并运行Bootloader+App”为例:

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


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

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

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

  • 触发条件与上下文不同:
    • 普通异常:发生在CPU运行时,硬件会自动压栈当前上下文(PC, PSR, 寄存器等),再跳转。
    • 复位异常:发生在CPU上电或复位时,没有需要保存的上下文(系统无状态)。硬件响应后,不压栈,直接跳转。
  • 向量表条目固定:在向量表中,第一个条目(偏移0x0)是MSP初值,第二个条目(偏移0x4)就是复位异常(异常1)的处理函数地址,即 Reset_Handler。这与其他异常(如中断号0对应偏移0x40)的定位方式完全相同:异常入口地址 = 向量表基地址 + 异常编号 × 4。
  • 使用相同的硬件路径:无论是复位、中断还是系统调用,CPU跳转到处理函数的硬件逻辑是同一套。复位只是这个通用机制的“首次调用”。
  • 在启动与重映射流程中的具体体现 整个启动与重映射过程,就是这套异常处理机制从“固定”到“可配置”的展现。 第一阶段:硬件强制执行的“初始异常响应” 这是复位异常最“特殊”的地方,因为它发生时,系统没有任何配置。

  • 默认的向量表基地址:内核硬件固定假设当前向量表基地址 VTOR = 0x00000000。这是复位异常响应的起点。
  • 芯片级物理映射(决定“0地址”是谁):BOOT引脚配置的硬件重映射,决定了物理上哪个存储器(主Flash、系统ROM、SRAM)占据了这个逻辑0地址。这相当于在硬件层面,为“零状态”的CPU预选了第一张异常向量表位于哪块物理介质上。
  • 执行固定响应序列: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来实现的。
  • 重定位向量表:在 Reset_Handler 中,软件可以将VTOR的值修改为新的基地址(如 0x08020000)。这个操作一旦完成,就重映射了整个异常系统。
    • 意义:此后,所有异常(包括复位、NMI、硬错误以及所有外设中断)的入口地址计算,都将基于新的基址。例如,此时如果发生中断#42,硬件将自动前往 0x08030000 + 0xA8 (42*4=0xA8) 处读取处理函数地址。
  • 在Bootloader/应用切换中的应用:
    • Bootloader运行在 0x08000000,其VTOR指向自身。
    • 当Bootloader准备跳转到位于 0x08020000 的应用程序时,它做两件核心事:
    • 设置VTOR:将VTOR改为 0x08020000,将异常系统指挥权交给应用程序。
    • 跳转到应用复位向量:直接读取应用程序向量表的第二个字(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函数的完整、详细的硬件与软件流程:

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

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

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

  • 加载初始主堆栈指针(MSP):
    • CPU内核发起对地址 0x00000000 的32位读取。
    • 通过上述硬件重映射,该请求被导向选定的物理存储器(例如 0x08000000)。
    • 从该地址读取到的4字节数据,被直接、无条件地装入MSP(R13) 寄存器。此值由软件开发者定义,并在此物理存储器的该位置。
  • 加载初始程序计数器(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。**
  • 执行跳转:PC寄存器被更新后,处理器在下个周期开始从新的PC地址取指并执行。这个地址,就是存储在物理存储器0x00000004(经重映射后)处的值,它指向了开发者编写并编译到该物理存储器特定位置的 Reset_Handler 函数的第一条指令。
  • 第四阶段:软件接管与系统重映射(Reset_Handler内) 10. Reset_Handler开始执行:CPU现在运行的是软件代码。该函数通常由芯片厂商提供的启动文件定义,用汇编或C语言编写。 11. 初始化关键系统:函数首先初始化系统时钟、配置Flash访问延迟,可能还会初始化少量必要的外设。 12. 关键操作——重配置向量表偏移寄存器(VTOR): – Reset_Handler通过一条存储指令(例如 SCB->VTOR = 0x08000000),向地址0xE000ED08(VTOR寄存器)写入一个新的基地址。 – 此操作具有全局和即时效应:写入完成后,Cortex-M3内核用于计算所有异常(包括复位、NMI、中断等)入口地址的基准点,从固定的0x00000000永久地更改为新的地址(例如0x08000000)。 – 意义:这实现了软件层面的逻辑重映射。系统从此完全脱离对“逻辑0地址”硬件映射的依赖,异常处理完全在统一的4GB地址空间中,由VTOR寄存器指向的向量表管理。这也是Bootloader能跳转到应用程序的核心机制:Bootloader在跳转前,将VTOR改为应用程序向量表的地址,从而将异常处理权移交给应用程序。 13. 初始化运行环境:Reset_Handler继续执行,将已初始化的全局变量(.data段)从Flash复制到SRAM,将未初始化的全局变量(.bss段)在SRAM中清零。 14. 跳转到主程序:最后,Reset_Handler调用标准的C入口函数 main() 或直接跳转到调度器,将控制权移交给用户应用程序。

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

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


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

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

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

  • 它计算 VTOR + 0(即 0x00000000),从该地址读取4字节数据,并将其作为初始主堆栈指针(MSP)加载。
  • 它计算 VTOR + 1*4(即 0x00000004),从该地址读取4字节数据(即复位异常向量),并将其作为程序计数器(PC)加载。 由于第一步所述的硬件映射,这两次读取实际上均来自物理地址 0x08000000 和 0x08000004。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启动的。它必须进行“初始化自检和确认”,明确建立自己所有核心资源的掌控权。这保证了:
    • 状态确定性:无论之前VTOR被设为何值(即使是调试器乱写的),应用程序都强制将其纠正到正确值。
    • 代码自包含与可移植性:这段初始化代码不依赖任何外部前提,使得该应用程序可以被任何兼容的引导程序启动,甚至由调试器直接加载到RAM中运行。
    • 逻辑清晰:在应用程序的源码中,可以清晰地看到其向量表基地址的配置,使得维护和调试变得直观。 补充:Bootloader跳转到应用程序的本质:Bootloader在跳转前所做的设置VTOR和加载MSP/PC这一系列操作,就是一次对CPU硬件复位启动序列的精确软件模拟: 在执行完 SCB->VTOR = 0x08002000 之后,Bootloader的代码立即执行以下关键操作来更新PC:
  • 读取应用程序的“复位向量”: Bootloader知道应用程序的向量表基址现在是0x08002000(它刚刚设置的)。它像CPU硬件在复位时做的那样,从这个基址计算出应用程序的入口地址:用入口地址 = 应用VTOR基址 + 4 = 0x08002004 。Bootloader执行一条加载指令,从地址0x08002004读取4个字节。这个读出的值,就是应用程序Reset_Handler函数的绝对地址(假设是0x08002100)。
  • 读取应用程序的初始栈指针(可选但推荐): 为了保证应用程序有一个完全干净的运行环境,Bootloader通常也会从应用程序向量表的第一个条目(0x08002000)读取初始主栈指针(MSP)的值,并用它来设置当前的MSP。这确保了应用程序从它自己设计的堆栈开始运行。
  • 执行跳转(更新PC): 这是最后一步,也是移交控制权的物理动作。Bootloader使用一条特殊的跳转指令,将上一步读出的应用程序Reset_Handler地址加载到PC寄存器。常用两种方法:
    • 直接加载PC:使用 LDR PC, [Rx] 指令(其中Rx寄存器存放着0x08002004读出的地址)。
    • 使用BX指令:将地址加载到一个通用寄存器(如R0),然后执行 BX R0。BX(分支并交换指令集)指令非常适合这种跳转。
  • 总结:VTOR的动作逻辑 VTOR在复位后的动作,遵循一条清晰的逻辑主线:从硬件强制的、隐含物理映射的默认值(0x00000000),被软件显式地重定义为明确、线性的物理基地址(如 0x08000000),并可在系统运行生命周期中,根据软件逻辑的需要进行动态重定位,以支持模块化、可升级的复杂系统设计。 整个过程体现了嵌入式系统从“硬件确定的起点”平稳过渡到“软件完全掌控的灵活运行”的核心设计哲学。


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

  • BOOT引脚电平被锁存:芯片根据BOOT0等引脚的电平,物理性地接通了内部一条电路。例如BOOT0=0时,逻辑地址0x00000000到0x0000FFFF这片区域的访问,被硬连线到了物理Flash存储器(0x08000000起始)的对应引脚上。这不是软件命令,是电子在硅片通道里的流向被改变了。
  • 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所在的准确位置,取回了第一条机器指令并开始执行。 至此,硬件的强制引导使命完成,控制权通过一条由物理电路决定的路径,被移交给了软件的第一行代码。
    赞(0)
    未经允许不得转载:网硕互联帮助中心 » Cortex-M3重启流程——笔记
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!