为ARM Cortex-M系列芯片编写Bootloader

- 日理万妓 2022-06-10 12:43 201阅读 0赞

https://zhuanlan.zhihu.com/p/25356501

为ARM Cortex-M系列芯片编写Bootloader

为ARM Cortex-M系列芯片编写Bootloader

王小军 王小军

6 个月前

为ARM Cortex-M系列芯片编写Bootloader

本文仅在ARM Cortex M3/M4芯片上进行过测试

1.引言

Bootloader用于用户程序的引导,其用途在于软件启动、固件升级等,Bootloader编写的核心内容是向量表的重定位。为了读者能够比较清晰了解Bootloader的机制,小军会说明CMSIS启动文件的机理,为此本文分为以下三个方面:

  • CMSIS启动文件简单分析
  • Bootlader的机理及实现
  • 向量表重定向的应用

2.CMSIS启动文件简单分析

下述为Adu360(ARM Cortex-M3内核)芯片的启动代码,由于符合CMSIS标准的启动文件大同小异,本文将以此启动文件作为示例,详细代码如下:

  1. Stack_Size EQU 0x00000400
  2. AREA STACK, NOINIT, READWRITE, ALIGN=3
  3. Stack_Mem SPACE Stack_Size
  4. __initial_sp
  5. Heap_Size EQU 0x00000200
  6. AREA HEAP, NOINIT, READWRITE, ALIGN=3
  7. __heap_base
  8. Heap_Mem SPACE Heap_Size
  9. __heap_limit
  10. PRESERVE8
  11. THUMB
  12. ; Vector Table Mapped to Address 0 at Reset
  13. AREA RESET, DATA, READONLY
  14. EXPORT __Vectors
  15. EXPORT __Vectors_End
  16. EXPORT __Vectors_Size
  17. __Vectors DCD __initial_sp ; Top of Stack
  18. DCD Reset_Handler ; Reset Handler
  19. DCD NMI_Handler ; The NMI handler
  20. ;中间省略若干个中断向量
  21. DCD PWM2_Int_Handler ; PWM2 [38]
  22. DCD 0 ; [39]
  23. __Vectors_End
  24. __Vectors_Size EQU __Vectors_End - __Vectors
  25. AREA |.text|, CODE, READONLY
  26. ; Reset handler
  27. Reset_Handler PROC
  28. EXPORT Reset_Handler [WEAK]
  29. IMPORT SystemInit
  30. IMPORT __main
  31. LDR R0, =SystemInit
  32. BLX R0
  33. LDR R0, =__main
  34. BX R0
  35. ENDP
  36. ; Dummy Exception Handlers (infinite loops which can be modified)
  37. NMI_Handler PROC
  38. EXPORT NMI_Handler [WEAK]
  39. B .
  40. ENDP
  41. ;中间省略若干弱引用的中断函数
  42. PWM2_Int_Handler
  43. B .
  44. ENDP
  45. ALIGN
  46. IF :DEF:__MICROLIB
  47. EXPORT __initial_sp
  48. EXPORT __heap_base
  49. EXPORT __heap_limit
  50. ELSE
  51. IMPORT __use_two_region_memory
  52. EXPORT __user_initial_stackheap
  53. __user_initial_stackheap
  54. LDR R0, = Heap_Mem
  55. LDR R1, =(Stack_Mem + Stack_Size)
  56. LDR R2, = (Heap_Mem + Heap_Size)
  57. LDR R3, = Stack_Mem
  58. BX LR
  59. ALIGN
  60. ENDIF
  61. END

启动程序还是比较简单的,其主要分为三个部分:

  • 堆栈的预分配
  • 中断向量表定义
  • 堆栈的初始化

ARM Cortex-M系列芯片堆栈的相关操作为C准备运行时环境,这里不做细说;中断向量表为芯片运行的根基,我们来重点分析一下中断向量表:

  • 中断向量表的起始为主栈指针(MSP)的初始值,用于在C运行时入口函数初始化堆栈之前建立基本的C运行时环境;
  • 紧接着是向量表的第一个中断向量复位向量,其不仅仅作为系统的一个中断存在,还是芯片执行程序的入口函数,其具体代码如下:

    ; Reset handler
    Reset_Handler PROC

    1. EXPORT Reset_Handler [WEAK]
    2. IMPORT SystemInit
    3. IMPORT __main
    4. LDR R0, =SystemInit
    5. BLX R0
    6. LDR R0, =__main
    7. BX R0
    8. ENDP

在复位向量中调用了SystemInit函数及__main函数,SystemInit函数为CMSIS定义的芯片初始化函数,用于对芯片的时钟等进行初始化配置,__main函数为C运行时库的入口函数,所以跳转到Reset_handler函数中可以启动应用;

  • 后面则是各种弱引用的中断向量,可以被C代码中同名的强引用定义取代;

一般,ARM Cortex-M芯片从存储器地址0x00000000处开始执行程序,通过__initial_sp初始化主栈指针,通过执行Reset_handler执行main函数中的程序。

2.Bootloader机理及其实现

如果我们希望升级系统中的固件或者从SD卡(或网络)中加载程序执行,需要执行如下步骤:

  • 启动Bootloader
  • 执行Bootloader中的任务加载用户程序
  • 切换到用户程序中执行 这时,我们就需要用到ARM Cortex-M内核的向量表重定向机制,其提供了一个中断向量表偏移寄存器(SCB->VTOR),改寄存器中的值为向量表的起始地址。

其示例程序为:

  1. /* 在调用此函数之前完成用户应用程序的加载和拷贝 */
  2. void retarget_vector(uint32_t new_addr)
  3. {
  4. __DMB(); /* 数据存储器屏障 */
  5. SCB->VTOR = new_addr;
  6. __DSB(); /* 数据同步屏障,保证在其后中断均为新的地址 */
  7. }

重定向之后,我们还需要跳转到用户程序中运行,其示例程序为:

  1. /* 在调用此函数之前完成重定向操作 */
  2. __asm void jump_to_application(uint32_t addr)
  3. {
  4. LDR SP, [R0] ; 设置用户程序MSP的值
  5. LDR PC, [R0, #4] ; 运行用户程序的Reset_handler
  6. }

在Bootloader的编写中还需要注意以下几点:

  • 固件采用hex格式还是bin格式

使用bin格式,因为hex文件附带地址信息,并不是我们需要的纯二进制文件;bin格式为按存储顺序的二进制文件

  • 注意ARM芯片的大小端字序

有可能存储设备和ARM芯片的大小端不一致,导致加载的程序不符合ARM芯片的大小端字序,从而导致程序无法运行;

  • VTOR的地址有条件限制

使用VTOR时需要将中断向量表的大小拓展到最近的2的幂次方,且新向量的基址必须要对齐到这个数值。

  • 什么样的固件才能升级

这里的固件升级对固件编译情况是有要求的,比如说想要把固件放到0x4000-0x20000的位置,固件在编译时需要设置其ROM的启动地址为0x4000,大小为0x1C000,bootloader复制代码到0x4000-0x20000,并且中断向量表重定向后固件才可以正常运行。

  • 跳转用户程序之前需要清理BootLoader使用过的资源

为了防止对用户程序产生副作用,Bootloader程序需要关闭所有使用过的芯片资源,最好关掉中断(感谢@望海楼提醒。 )

Bootloader的实现过程可以总结如下(详细见《ARM Cortex-M3 与 Cortex-M4 权威指南》):利用启动ROM中的向量表启动Bootloader

  1. 启动Bootloader中的任务,完成用户程序的加载
  2. 设置VTOR指向用户程序存储器中的向量表
  3. 跳转到用户程序存储器的向量表中的中断向量

3.向量表重定向的应用

  • Bootloader的编写
  • 应用程序加载到RAM中执行,其类似于Bootloader
  • 动态修改向量表,有些情况下ROM中可能会有一个中断的多个处理示例,可能在应用的不同阶段在它们之间切换,这种情况下,可以将向量表从程序存储中复制到RAM中,设置VTOR指向RAM中的向量表,RAM中的内容可以任意时间修改,因此可以在应用的不同阶段使用不同的中断向量。

此部分详细见《ARM Cortex-M3 与 Cortex-M4 权威指南》

发表评论

表情:
评论列表 (有 0 条评论,201人围观)

还没有评论,来说两句吧...

相关阅读

    相关 ARM芯片名字关系

    前言   一款ARM芯片相关的名字太多,经常搞不清楚都分别代表什么,本文就是梳理一下名字的含义层次并着重回顾一下芯片架构。 ![在这里插入图片描述][watermar