BootLoader的理解与实现

笔者今天简单谈谈BootLoader的理解,本文基于CortexM3/M4来讲讲。

1、BootLoader与APP

  • BootLoader、APP(用户应用)都是一个程序,其功能不同。
  • BootLoader 功能就是更新用户程序,用户程序就是实现功能的程序(比如记录温度等数据),BootLoader主要功能就是程序引导,在程序引导前可能会做一些硬件初始化,做好了之后再进入用户程序,这样的话 用户程序就直接运行就OK。
  • 常见的BootLoader 比如在linux当中用的Uboot(Universal BootLoader),Windows当中的BIOS程序,都是引导程序
  • Bootloader当前可能还有些其他功能,比如加密这种等等。
  • BootLoader更新用户程序,BootLoader可以通过Nand、eMMC、U盘和SD卡进行程序更新, 比如windows 从U盘驱动,然后更新C盘的系统,从而执行程序的更新。
  • BootLoader的更新方式也有很多种,通过USB/CAN/串口/SPI/I2C/TCPIP网络通信的方式这种去更新。

在这里插入图片描述

2、BootLoader升级的文件格式

上位机通过与BootLoader交互把程序写到固定位置,程序进入BootLoader之后,直接跳到对应的地址就可以执行。

(IAP 在线升级)程序的download 文件格式一般 有两种方式,一种bin文件格式,一种hex文件格式

2.1 bin文件格式升级

bin文件没什么格式,完全就是二进制数据,对,就是数据,比如Code 段、ro data段 、rw data段,BootLoader接收到数据之后,写到约定的地址就可以。

**注意:**如果有多个段地址 比如data的地址和Code的地址有区别 Code在0x0800 0000 Data在0x2000 0000,那么生成的bin 文件只是写到0x0800 0000 data的数据地址 不正确,程序如何运行起来的呢?这就是要靠分散加载来实现。

将Code段搬到对应的地址执行,BSS段初始化为0,RW初始化为相应的值。
详情:分散加载启动

2.2 hex文件格式升级

bin升级的一个不好的地方就是要指定烧录地址,即加载地址,因为bin 都是raw data,没有任何信息,而hex文件则不一样 其带地址信息,同时有校验信息,更加准确。

hex文件烧录的时候 上位机需要解析hex文件 提取出数据 然后再download 进行。hex文件格式参考。
在这里插入图片描述

3、BootLoader程序的跳转与执行

  • BootLoader可以跳转内部SRAM外部SRAM或者DDR上面去执行程序
  • BootLoader可以跳转 到 Nor FlashQSPI Flash以及支持并口的Nor Flash运行程序APP
  • 高阶的玩法直接动态加载APP,APP程序运行与处理器无关,全部采用系统API,比如windows上直接运行exe程序。
  • BootLoader程序的跳转也很方便,直接将PC指针指向APP程序地址即可(嵌入式程序一般是中断向量表地址
  • app 地址首地址一般是栈的地址(SP),(SP+4)是属于复位向量的地址,可以去执行,最终跳到app的main函数。
  • app 需要 设置中断向量表的地址,同时链接脚本指定code的存放位置

以stm32的为示例:
(1)设置指定code的链接地址(0x08000000+ 8000 (offset))。

LR_IROM1 0x08008000 0x00080000  {    ; load region size_region
  ER_IROM1 0x08008000 0x00080000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20000000 NOCOMPRESS 0x00010000  {  ; RW data
   .ANY (+RW +ZI)
  }
}

(2)设置中断向量表的偏移地址

#define VECT_TAB_OFFSET 8000
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */

(3)bootloader跳转程序(到app)如下:

u32 ApplicationAddress = 0x08008000;  //app 地址 
typedef  void (*pFunction)(void);     //函数指针
void Jump_Used_Main(void)
{
	printf("\r\nboot2-------- Jump APP --------- \r\n");
	/*	判断栈顶 是否位于 0x20000000  128K 	  */
   	if (  ((*(__IO uint32_t*)ApplicationAddress) >= 0x20000000) &&((*(__IO uint32_t*)ApplicationAddress) <= 0x20010000))
   	{ 	
     		JumpAddress = *(__IO uint32_t*) (ApplicationAddress +4);
     		Jump_To_Application = (pFunction) JumpAddress;
     		/*close the interrupt*/
     		_disable_interrupr();
     		__set_MSP(*(__IO uint32_t*) ApplicationAddress);
     		Jump_To_Application();
   	}
	else
	{			
		while(1)
		{
			printf("\r\nboot2  error\r\n");
		}
	}
}

4、CortexM4的异常向量表

在这里插入图片描述
CortexM4 程序一般从0x0地址开始运行,取出堆栈地址即栈顶,然后进入reset handler 函数,进行初始化,进行分散加载,及程序开始执行的地址,最后进入main函数。

中断向量表的地址可以进行偏移,所以APP程序的中断向量表可以通过偏移来实现中断地址的获取,中断Code 段的链接地址,同时也需要偏移,这是通过链接脚本指定。

总结下来就两点

  • 中断向量表偏移
  • Code段的地址偏移,通过链接脚本放在指定位置(对于APP来说)

5、CortexM4的内存映射布局

在这里插入图片描述
Cortex M4的的memory 布局如上图,对于32位地址来说,各区域的地址都有对应的布局,不可以随便放。

  • 0~0x1FFF FFFF 主要是放Code ,
    • 0 ~ 0x 000F FFFF 可能是Flash 或者 系统存储空间(系统Boot 代自带码,适用于串口烧录) 或者SRAM空间(方便从SRAM启动代码)
    • 0x0800 0000 ~0x 080F FFFF Flash空间 Max是1M,正好与0 ~ 0x 000F FFFF地址对应,所以STM32的起始地址从0x08000000 做了地址重映射
    • 0x1000 0000 ~ 0x 1000 FFFF CCM RAM空间,(方可以放DATA数据)
  • 0x2000 0000 ~ 0x3FFF FFFF 是SRAM空间 (128K)
    • 0x2000 0000 ~ 0x2001 BFFF 112K RAM空间
    • 0x2000 C000 ~ 0x2001 FFFF 16K RAM空间
  • 0x4000 0000 ~ 0x5FFF FFF 外设地址空间
    • ADC UART GPIO 等外设接口

6、扇区的理解

STM32Flash的理解

7、代码的实现

BootLoader与APP的关系
在这里插入图片描述
比如实现的过程中

  • 先烧录bootloader bin,烧录地址指定(0x800 0000),编译链接时链接脚本指定Code和DATA位置 (同样是0x800 0000)
  • 再烧录APP bin,烧录地址指定(0x800 0000 + offset)编译链接时链接脚本指定Code和DATA位置(同样是0x800 0000 + offset)
  • 程序执行在bootloader得时候,先判断APP是否存在,然后在跳转,不然直接跳转,程序直接飞掉了
  • APP程序复位得时候,会跳到bootloader,这是因为程序从0地址开始执行
  • 当然可以将APP 和 bootloader 的代码打包在一起,然后一起烧录到固件当中,当然这种是通过烧录工具去烧录
  • 在bootloader的时候,可以APP的Code 可以被更新,之后APP上电时,上电的时分散加载会把数据段加载到SRAM中

当然也可以有多个APP程序(只要Flash空间大)
在这里插入图片描述
具体代码实现:
bootloader工程
APP工程

8、附录

本文简单介绍一下NXP LPC54XX系列的启动方式。
(1)存储空间分布图
在这里插入图片描述

  • 0x0000 0000 to 0x1FFF FFFF:

    • SRAMX:位于指令和数据总线上面(I-Code D-Code),可以执行CortexM4 code,可以用于堆栈区域。192KB。
    • BootROM:内部的Flash区域,存储厂商的boot code。64KB。
    • SPIFI:SPIFI 内存映射区域,可以直接通过总线访问,一般是外部的Flash区域。128MB。
  • 0x2000 0000 to 0x3FFF FFFF:

    • Main SRAM:主内存区域,用于存放data数据,比如rw+bss段
    • SRAM bit band:SRAM位带区,
  • 0x4000 0000 to 0x7FFF FFFF:

    • 外设地址区域(包括外设位带区)
      位带区解释
      Cortex™-M4 存储器映像包括两个位带(bit-band)区,一个是SRAM区,另一个是片上外设区。由于不能直接对一个位进行操作,为了实现对寄存器进行快速的位操作,设计了两个别名区。两个位带区将别名区中的每个字映射到位带区的一个位,在别名区写入一个字具有对位带区的目标位执行读-改-写操作的相同效果。
      在这里插入图片描述
  • 0x8000 0000 to 0xDFFF FFFF:片外的memory,通过外部内存控制器控制(EMC),支持RAM、ROM以及Flash,还包括SDRAM。

  • 0xE000 0000 to 0xE00F FFFF:Cortex-M4私有的外设总线

(2)启动方式
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • EMC boot 模式,例如外部并行flash启动
  • ISP 模式,利用Boot rom的code,通过usb/uart/iic/spi 的通信方式下载到sram,从而启动。
  • flash模式,通过uart/iic/spi 或者spifi,将外部flash的code搬到sram运行。

注意:
1、nxp的LPC64系列,内存flash只够存放boot rom的code,所以需要外部flash来存放code,不然一掉电数据就丢了,一般是到sramx 运行,sram0-3用来存放数据。
2、sramx用来执行code,总共就192k的区域,如果code空间超过这个,那么就放不下code,需要另谋他路了。
3、如果从spi flash启动执行,sramx 以及 sram0-3用来存放code,那么这样空间就足够了,就是需要从外部flash启动,并读取code。

在这里插入图片描述

  • boot rom从外部flash般code 的时候,也会做相关的校验,比如header以及crc。

  • 首先会从外部flash般512byte,核对Image marker,在中断向量表的0x24位置,该位置恰好保留,可以用户自定义,0xEDDC94BD,特定字符。然后0x28 指向了一段特定数据区域的地址,存放nxp自己的校验数据等,比如CRC这些。

  • 特定数据区域同样有header,

    • Header Value:0xFEED5A5A
    • Image Type:0 :CRC Check,1:No CRC Check。
    • Load address:sramx启动:0x0 sram0-3:0x20000000,外部SPI Flash启动:0x10000000 (SPIFI)并行Flash启动:0x80000000
    • Image length:Image Length-4
    • CRC:crc check value。
      (3)中断向量表
      在这里插入图片描述
  • 经过上面解释就知道了,为什么中断向量表中有这么多奇怪的数据,是NXP厂商自定义的,本身CortexM4架构中就保留了这些字段。

  • 用户外设的中断向量表(非异常向量表)的个数也不一定都是0-255,个数也是厂商定义的。

__attribute__ ((used, section(".isr_vector")))
void (* const g_pfnVectors[])(void) = {
   // Core Level - CM4
   &_vStackTop,                       // The initial stack pointer
   ResetISR,                          // The reset handler
   NMI_Handler,                       // The NMI handler
   HardFault_Handler,                 // The hard fault handler
   MemManage_Handler,                 // The MPU fault handler
   BusFault_Handler,                  // The bus fault handler
   UsageFault_Handler,                // The usage fault handler
   __valid_user_code_checksum,        // LPC MCU checksum
   0,                                 // ECRP
   (void (*)(void))0xEDDC94BD,        // Reserved
   (void (*)(void))0x160,             // Reserved
   SVC_Handler,                       // SVCall handler
   DebugMon_Handler,                  // Debug monitor handler
   0,                                 // Reserved
   PendSV_Handler,                    // The PendSV handler
   SysTick_Handler,                   // The SysTick handler

   // Chip Level - LPC54018
   WDT_BOD_IRQHandler,         // 16: Windowed watchdog timer, Brownout detect
   DMA0_IRQHandler,            // 17: DMA controller
   GINT0_IRQHandler,           // 18: GPIO group 0
   GINT1_IRQHandler,           // 19: GPIO group 1
   PIN_INT0_IRQHandler,        // 20: Pin interrupt 0 or pattern match engine slice 0
   PIN_INT1_IRQHandler,        // 21: Pin interrupt 1or pattern match engine slice 1
   PIN_INT2_IRQHandler,        // 22: Pin interrupt 2 or pattern match engine slice 2
   PIN_INT3_IRQHandler,        // 23: Pin interrupt 3 or pattern match engine slice 3
   UTICK0_IRQHandler,          // 24: Micro-tick Timer
   MRT0_IRQHandler,            // 25: Multi-rate timer
   CTIMER0_IRQHandler,         // 26: Standard counter/timer CTIMER0
   CTIMER1_IRQHandler,         // 27: Standard counter/timer CTIMER1
   SCT0_IRQHandler,            // 28: SCTimer/PWM
   CTIMER3_IRQHandler,         // 29: Standard counter/timer CTIMER3
   FLEXCOMM0_IRQHandler,       // 30: Flexcomm Interface 0 (USART, SPI, I2C, FLEXCOMM)
   FLEXCOMM1_IRQHandler,       // 31: Flexcomm Interface 1 (USART, SPI, I2C, FLEXCOMM)
   FLEXCOMM2_IRQHandler,       // 32: Flexcomm Interface 2 (USART, SPI, I2C, FLEXCOMM)
   FLEXCOMM3_IRQHandler,       // 33: Flexcomm Interface 3 (USART, SPI, I2C, FLEXCOMM)
   FLEXCOMM4_IRQHandler,       // 34: Flexcomm Interface 4 (USART, SPI, I2C, FLEXCOMM)
   FLEXCOMM5_IRQHandler,       // 35: Flexcomm Interface 5 (USART, SPI, I2C,, FLEXCOMM)
   FLEXCOMM6_IRQHandler,       // 36: Flexcomm Interface 6 (USART, SPI, I2C, I2S,, FLEXCOMM)
   FLEXCOMM7_IRQHandler,       // 37: Flexcomm Interface 7 (USART, SPI, I2C, I2S,, FLEXCOMM)
   ADC0_SEQA_IRQHandler,       // 38: ADC0 sequence A completion.
   ADC0_SEQB_IRQHandler,       // 39: ADC0 sequence B completion.
   ADC0_THCMP_IRQHandler,      // 40: ADC0 threshold compare and error.
   DMIC0_IRQHandler,           // 41: Digital microphone and DMIC subsystem
   HWVAD0_IRQHandler,          // 42: Hardware Voice Activity Detector
   USB0_NEEDCLK_IRQHandler,    // 43: USB Activity Wake-up Interrupt
   USB0_IRQHandler,            // 44: USB device
   RTC_IRQHandler,             // 45: RTC alarm and wake-up interrupts
   FLEXCOMM10_IRQHandler,      // 46: Flexcomm Interface 10 (SPI, FLEXCOMM)
   Reserved47_IRQHandler,      // 47: Reserved interrupt
   PIN_INT4_IRQHandler,        // 48: Pin interrupt 4 or pattern match engine slice 4 int
   PIN_INT5_IRQHandler,        // 49: Pin interrupt 5 or pattern match engine slice 5 int
   PIN_INT6_IRQHandler,        // 50: Pin interrupt 6 or pattern match engine slice 6 int
   PIN_INT7_IRQHandler,        // 51: Pin interrupt 7 or pattern match engine slice 7 int
   CTIMER2_IRQHandler,         // 52: Standard counter/timer CTIMER2
   CTIMER4_IRQHandler,         // 53: Standard counter/timer CTIMER4
   RIT_IRQHandler,             // 54: Repetitive Interrupt Timer
   SPIFI0_IRQHandler,          // 55: SPI flash interface
   FLEXCOMM8_IRQHandler,       // 56: Flexcomm Interface 8 (USART, SPI, I2C, FLEXCOMM)
   FLEXCOMM9_IRQHandler,       // 57: Flexcomm Interface 9 (USART, SPI, I2C, FLEXCOMM)
   SDIO_IRQHandler,            // 58: SD/MMC
   CAN0_IRQ0_IRQHandler,       // 59: CAN0 interrupt0
   CAN0_IRQ1_IRQHandler,       // 60: CAN0 interrupt1
   CAN1_IRQ0_IRQHandler,       // 61: CAN1 interrupt0
   CAN1_IRQ1_IRQHandler,       // 62: CAN1 interrupt1
   USB1_IRQHandler,            // 63: USB1 interrupt
   USB1_NEEDCLK_IRQHandler,    // 64: USB1 activity
   ETHERNET_IRQHandler,        // 65: Ethernet
   ETHERNET_PMT_IRQHandler,    // 66: Ethernet power management interrupt
   ETHERNET_MACLP_IRQHandler,  // 67: Ethernet MAC interrupt
   Reserved68_IRQHandler,      // 68: Reserved interrupt
   LCD_IRQHandler,             // 69: LCD interrupt
   SHA_IRQHandler,             // 70: SHA interrupt
   SMARTCARD0_IRQHandler,      // 71: Smart card 0 interrupt
   SMARTCARD1_IRQHandler,      // 72: Smart card 1 interrupt

   0,                          // Reserved
   0,                          // Reserved
   0,                          // Reserved
   0,                          // Reserved
   0,                          // Reserved
   0,                          // Reserved
   0,                          // Reserved
   0,                          // Reserved
   0,                          // Reserved
   0,                          // Reserved
   0,                          // Reserved
   0,                          // Reserved
   0,                          // Reserved
   0,                          // Reserved
   0,                          // Reserved
   (void (*)(void))0xFEEDA5A5, // Header Marker

#if defined (ADD_CRC)
   (__imghdr_imagetype - 1),   // (0x04) Image Type
    __imghdr_loadaddress,      // (0x08) Load_address
#else
   __imghdr_imagetype,         // (0x04) Image Type
   __imghdr_loadaddress,       // (0x08) Load_address
#endif
   (void (*)(void))(((unsigned)_image_size) - 4),            // (0x0C) load_length, exclude 4 bytes CRC field.
   0,                          // (0x10) CRC value (only applicable to NON Non-secure images).
   0,                          // (0x14) Version (only applicable to DUAL_ENH image type.
   0,                          // (0x18) EMC static memory configuration settings, required for EMC boot
   (void (*)(void))IMG_BAUDRATE,       // (0x1C) image baudrate
   0,                          // (0x20) reserved
   (void (*)(void))0xEDDC94BD,         // (0x24) Image_marker
   0,                          // (0x28) SBZ
   0,                          // (0x2C) reserved
   #ifdef W25Q128JVFM
   /* SPIFI Descriptor - W25Q128JVFM */
   (void (*)(void))0x00000000,         // 0xFFFFFFFF to default 1-bit SPI mode  ;DevStrAdr
   (void (*)(void))0x001870EF,         // mfgId + extCount
   (void (*)(void))0x00000000,         // extid 0-3
   (void (*)(void))0x00000000,         // extid 4-7
   (void (*)(void))0x0001001D,         // caps
   (void (*)(void))0x00000100,         // Blks + RESV1
   (void (*)(void))0x00010000,         // blkSize
   (void (*)(void))0x00000000,         // subBlks + subBlkSize
   (void (*)(void))0x00000100,         // pageSize + RESV2
   (void (*)(void))0x00003F00,         // maxReadSize
   (void (*)(void))0x68506850,         // maxClkRate,maxReadRate,maxHSReadRate,maxProgramRate
   (void (*)(void))0x04030050,         // maxHSProgramRate,initDeInitFxId,clearStatusFxId,getStatusFxId,
   (void (*)(void))0x14110D09,         // setStatusFxId,setOptionsFxId,getReadCmdFxId,getWriteCmdFxId
   #endif

    #ifdef MXL12835F
    /* SPI Descriptor - MXL12835F */
   (void (*)(void))0x00000000,         // 0xFFFFFFFF to default 1-bit SPI mode  ;DevStrAdr
   (void (*)(void))0x001820C2,         // mfgId + extCount
   (void (*)(void))0x00000000,         // extid 0-3
   (void (*)(void))0x00000000,         // extid 4-7
   (void (*)(void))0x0001001D,         // caps
   (void (*)(void))0x00000100,         // Blks + RESV1
   (void (*)(void))0x00010000,         // blkSize
   (void (*)(void))0x00000000,         // subBlks + subBlkSize
   (void (*)(void))0x00000100,         // pageSize + RESV2
   (void (*)(void))0x00003F00,         // maxReadSize
   (void (*)(void))0x68506850,         // maxClkRate,maxReadRate,maxHSReadRate,maxProgramRate
   (void (*)(void))0x06030050,         // maxHSProgramRate,initDeInitFxId,clearStatusFxId,getStatusFxId
   (void (*)(void))0x14110F0B,         // setStatusFxId,setOptionsFxId,getReadCmdFxId,getWriteCmdFxId
   #endif

}; /* End of g_pfnVectors */

(4)链接脚本
gcc 链接脚本语法,待完善

  • 13
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张一西

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值