基于某个硬件平台做bootloader前应当先了解平台架构,每个硬件平台架构不同,跳转方式、写入app方式也不尽相同。

RT1052架构

启动方式

RT1052有多种启动方式,比如SD卡、各类的Flash,主要分为两大类:
1、直接从外部存储设备原地XIP执行,针对的是FlexSPI NORFlash。
2、从外部存储设备加载代码到指定RAM中执行,比如各类nand falsh,emmc,当然NOR Flash也是可以的。
所支持的启动设备种类:
RT1052 Bootloader开发笔记(1)--准备工作 - 图1

镜像结构

目前计划在nor flash上使用XIP(eXecuted In Place)方式运行代码,下图是要写到flash中的镜像结构:
RT1052 Bootloader开发笔记(1)--准备工作 - 图2

FDCB:Flash Device Configuration Block

地址范围:0x0000 0000-0x0000 1000
FCB(FlexSPI Configuration block)地址:0x0000 0000-0x0000 0200。
MCU通过读取FCB处的配置,重新初始化FlexSPI,进而使读写外部flash达到最优性能。
当然,MCU最初就是能够读取外部flash的,RT1052为了最大限度的支持更多的flash,是使用的内部寄存器默认值去读取的,为了保持最大的兼容性,其默认值的性能并非最佳性能,只是确保能将数据从flash芯片中读出即可,速率很低(30M),读出FCB数据后,MCU会对FlexSPI进行重新配置。并且重新初始化Flash以达到最佳性能。用户可根据自己使用的flash品牌参数性能等,配置FCB。
在程序中FCB实际就是一个结构体,赋值后编译到FCB所在区域即可(0x0000 0000-0x0000 0200)。
RT1052 Bootloader开发笔记(1)--准备工作 - 图3

2、IVT: Image Vector Table

地址:0x0000 1000-0x0000 1020
这是一个地址向量表,记录了各个配置块和APP的地址信息。
每个参数都占4Byte:
RT1052 Bootloader开发笔记(1)--准备工作 - 图4

  1. #if defined(XIP_BOOT_HEADER_ENABLE) && (XIP_BOOT_HEADER_ENABLE == 1)
  2. #if defined(__CC_ARM) || defined(__GNUC__)
  3. __attribute__((section(".boot_hdr.ivt")))
  4. #elif defined(__ICCARM__)
  5. #pragma location=".boot_hdr.ivt"
  6. #endif
  7. /*************************************
  8. * IVT Data
  9. *************************************/
  10. const ivt image_vector_table = {
  11. IVT_HEADER, /* IVT 头部 */
  12. IMAGE_ENTRY_ADDRESS, /* 固件入口地址 */
  13. IVT_RSVD, /* 保留 */
  14. (uint32_t)DCD_ADDRESS, /* DCD数据存储的地址 */
  15. (uint32_t)BOOT_DATA_ADDRESS, /* 启动数据结构存储的地址 */
  16. (uint32_t)&image_vector_table, /* IVT自身指针的存储地址(绝对地址) */
  17. (uint32_t)CSF_ADDRESS, /* 命令序列文件存储的地址,用于加密启动 */
  18. IVT_RSVD /* 保留 */
  19. };

BD: Boot data structure

地址:0x0000 1020-0x0000 1030
存储要加载到的目的地址和要加载的长度,这个很重要,可以实现程序链接到哪个位置。
RT1052 Bootloader开发笔记(1)--准备工作 - 图5

  1. /*************************************
  2. * Boot Data
  3. *************************************/
  4. const BOOT_DATA_T boot_data = {
  5. FLASH_BASE, /* 固件的启动地址,即flash的首地址 */
  6. FLASH_SIZE, /* flash大小 */
  7. PLUGIN_FLAG, /* 插件函数标志,用于加密启动*/
  8. 0xFFFFFFFF /* 空,额外数据字 */
  9. };

DCD: Device Configuration Data

地址:0x1030-0x2000
配置SEMC、外部SDRAM等。 MCU可根据DCD中数据自动初始化这些外部组件。
RT1052 Bootloader开发笔记(1)--准备工作 - 图6

APP

地址:0x2000 -
应用程序。

bin文件存放地址是从Flash 0x00开始的,映射到芯片是地址0x6000 0000.
0x6000 0000—0x6000 2000范围内是XIP头部信息,也就是被以上FCB、DCD等所占据,用户的应用程序应从0x6000 2000开始。

RT1052 Bootloader开发笔记(1)--准备工作 - 图7

问题

中断向量表

问题:

在调试过程中顺利跳转到了app区域,在app中并未做中断向量表偏移操作,但是串口命令行居然运行正常。通常情况下,如果中断向量表不做重置得话,当触发中断(如串口接收中断)时,就会跳转到 bootloader 区域得串口服务函数地址,造成异常。
在 STM32 上,进入APP后,通常这么操作:

  1. #define RT_APP_PART_ADDR 0x08020000
  2. static int ota_app_vtor_reconfig(void)
  3. {
  4. #define NVIC_VTOR_MASK 0x3FFFFF80
  5. /* Set the Vector Table base location by user application firmware definition */
  6. SCB->VTOR = RT_APP_PART_ADDR & NVIC_VTOR_MASK;
  7. return 0;
  8. }
  9. INIT_BOARD_EXPORT(ota_app_vtor_reconfig);

及早调用 ota_app_vtor_reconfig 内得代码,来完成中断向量表得重置。而在 RT1052 中并未发现类似操作,却能正常运行,同时也查看了NXP官方的bootloader及firmware 例程 sblsfw ,也未发现此类操作。

解决:

通过查看ARM内核 SCB 的地址信息,最终定位到了启动文件中的代码片段:

  1. __Vectors DCD |Image$$ARM_LIB_STACK$$ZI$$Limit| ; Top of Stack
  2. DCD Reset_Handler ; Reset Handler
  3. DCD NMI_Handler ;NMI Handler
  4. DCD HardFault_Handler ;Hard Fault Handler
  5. DCD MemManage_Handler ;MPU Fault Handler
  6. DCD BusFault_Handler ;Bus Fault Handler
  7. DCD UsageFault_Handler ;Usage Fault Handler
  8. DCD 0 ;Reserved
  9. DCD 0 ;Reserved
  10. DCD 0 ;Reserved
  11. DCD 0 ;Reserved
  12. DCD SVC_Handler ;SVCall Handler
  13. DCD DebugMon_Handler ;Debug Monitor Handler
  14. DCD 0 ;Reserved
  15. DCD PendSV_Handler ;PendSV Handler
  16. DCD SysTick_Handler ;SysTick Handler
  17. ;External Interrupts
  18. DCD DMA0_DMA16_IRQHandler ;DMA channel 0/16 transfer complete
  19. DCD DMA1_DMA17_IRQHandler ;DMA channel 1/17 transfer complete
  20. DCD DMA2_DMA18_IRQHandler ;DMA channel 2/18 transfer complete
  21. ... ...;省略部分中断服务函数入口
  22. DCD DefaultISR ;254
  23. DCD 0xFFFFFFFF ; Reserved for user TRIM value
  24. __Vectors_End
  25. __Vectors_Size EQU __Vectors_End - __Vectors
  26. AREA |.text|, CODE, READONLY
  27. ; Reset Handler
  28. Reset_Handler PROC
  29. EXPORT Reset_Handler [WEAK]
  30. IMPORT SystemInit
  31. IMPORT __main
  32. CPSID I ; Mask interrupts
  33. LDR R0, =0xE000ED08 ;SCB->VTOR 中断向量表地址赋值给 R0
  34. LDR R1, =__Vectors ;取中断限量表地址给 R1__Vectors在上面已定义
  35. STR R1, [R0] ;将 R1 中的数据赋值给 R0 指向的存储空间,实际上就是将中断向量表的首地址给SCB->VTOR
  36. LDR R2, [R1] ;
  37. MSR MSP, R2
  38. LDR R0, =SystemInit
  39. BLX R0
  40. CPSIE i ; Unmask interrupts
  41. LDR R0, =__main
  42. BX R0
  43. ENDP

通过 L44 — L46 完成了中断向量表的偏移。