基于某个硬件平台做bootloader前应当先了解平台架构,每个硬件平台架构不同,跳转方式、写入app方式也不尽相同。
RT1052架构
启动方式
RT1052有多种启动方式,比如SD卡、各类的Flash,主要分为两大类:
1、直接从外部存储设备原地XIP执行,针对的是FlexSPI NORFlash。
2、从外部存储设备加载代码到指定RAM中执行,比如各类nand falsh,emmc,当然NOR Flash也是可以的。
所支持的启动设备种类:
镜像结构
目前计划在nor flash上使用XIP(eXecuted In Place)方式运行代码,下图是要写到flash中的镜像结构:
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)。
2、IVT: Image Vector Table
地址:0x0000 1000-0x0000 1020
这是一个地址向量表,记录了各个配置块和APP的地址信息。
每个参数都占4Byte:
#if defined(XIP_BOOT_HEADER_ENABLE) && (XIP_BOOT_HEADER_ENABLE == 1)
#if defined(__CC_ARM) || defined(__GNUC__)
__attribute__((section(".boot_hdr.ivt")))
#elif defined(__ICCARM__)
#pragma location=".boot_hdr.ivt"
#endif
/*************************************
* IVT Data
*************************************/
const ivt image_vector_table = {
IVT_HEADER, /* IVT 头部 */
IMAGE_ENTRY_ADDRESS, /* 固件入口地址 */
IVT_RSVD, /* 保留 */
(uint32_t)DCD_ADDRESS, /* DCD数据存储的地址 */
(uint32_t)BOOT_DATA_ADDRESS, /* 启动数据结构存储的地址 */
(uint32_t)&image_vector_table, /* IVT自身指针的存储地址(绝对地址) */
(uint32_t)CSF_ADDRESS, /* 命令序列文件存储的地址,用于加密启动 */
IVT_RSVD /* 保留 */
};
BD: Boot data structure
地址:0x0000 1020-0x0000 1030
存储要加载到的目的地址和要加载的长度,这个很重要,可以实现程序链接到哪个位置。
/*************************************
* Boot Data
*************************************/
const BOOT_DATA_T boot_data = {
FLASH_BASE, /* 固件的启动地址,即flash的首地址 */
FLASH_SIZE, /* flash大小 */
PLUGIN_FLAG, /* 插件函数标志,用于加密启动*/
0xFFFFFFFF /* 空,额外数据字 */
};
DCD: Device Configuration Data
地址:0x1030-0x2000
配置SEMC、外部SDRAM等。 MCU可根据DCD中数据自动初始化这些外部组件。
APP
地址:0x2000 -
应用程序。
bin文件存放地址是从Flash 0x00开始的,映射到芯片是地址0x6000 0000.
0x6000 0000—0x6000 2000范围内是XIP头部信息,也就是被以上FCB、DCD等所占据,用户的应用程序应从0x6000 2000开始。
问题
中断向量表
问题:
在调试过程中顺利跳转到了app区域,在app中并未做中断向量表偏移操作,但是串口命令行居然运行正常。通常情况下,如果中断向量表不做重置得话,当触发中断(如串口接收中断)时,就会跳转到 bootloader 区域得串口服务函数地址,造成异常。
在 STM32 上,进入APP后,通常这么操作:
#define RT_APP_PART_ADDR 0x08020000
static int ota_app_vtor_reconfig(void)
{
#define NVIC_VTOR_MASK 0x3FFFFF80
/* Set the Vector Table base location by user application firmware definition */
SCB->VTOR = RT_APP_PART_ADDR & NVIC_VTOR_MASK;
return 0;
}
INIT_BOARD_EXPORT(ota_app_vtor_reconfig);
及早调用 ota_app_vtor_reconfig 内得代码,来完成中断向量表得重置。而在 RT1052 中并未发现类似操作,却能正常运行,同时也查看了NXP官方的bootloader及firmware 例程 sbl 、sfw ,也未发现此类操作。
解决:
通过查看ARM内核 SCB 的地址信息,最终定位到了启动文件中的代码片段:
__Vectors DCD |Image$$ARM_LIB_STACK$$ZI$$Limit| ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ;NMI Handler
DCD HardFault_Handler ;Hard Fault Handler
DCD MemManage_Handler ;MPU Fault Handler
DCD BusFault_Handler ;Bus Fault Handler
DCD UsageFault_Handler ;Usage Fault Handler
DCD 0 ;Reserved
DCD 0 ;Reserved
DCD 0 ;Reserved
DCD 0 ;Reserved
DCD SVC_Handler ;SVCall Handler
DCD DebugMon_Handler ;Debug Monitor Handler
DCD 0 ;Reserved
DCD PendSV_Handler ;PendSV Handler
DCD SysTick_Handler ;SysTick Handler
;External Interrupts
DCD DMA0_DMA16_IRQHandler ;DMA channel 0/16 transfer complete
DCD DMA1_DMA17_IRQHandler ;DMA channel 1/17 transfer complete
DCD DMA2_DMA18_IRQHandler ;DMA channel 2/18 transfer complete
... ...;省略部分中断服务函数入口
DCD DefaultISR ;254
DCD 0xFFFFFFFF ; Reserved for user TRIM value
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors
AREA |.text|, CODE, READONLY
; Reset Handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
CPSID I ; Mask interrupts
LDR R0, =0xE000ED08 ;SCB->VTOR 中断向量表地址赋值给 R0
LDR R1, =__Vectors ;取中断限量表地址给 R1,__Vectors在上面已定义
STR R1, [R0] ;将 R1 中的数据赋值给 R0 指向的存储空间,实际上就是将中断向量表的首地址给SCB->VTOR
LDR R2, [R1] ;
MSR MSP, R2
LDR R0, =SystemInit
BLX R0
CPSIE i ; Unmask interrupts
LDR R0, =__main
BX R0
ENDP
通过 L44 — L46 完成了中断向量表的偏移。