Chinaunix首页 | 论坛 | 博客
  • 博客访问: 30995420
  • 博文数量: 230
  • 博客积分: 2868
  • 博客等级: 少校
  • 技术积分: 2223
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-08 21:48
个人简介

Live & Learn

文章分类

全部博文(230)

文章存档

2022年(2)

2019年(5)

2018年(15)

2017年(42)

2016年(24)

2015年(13)

2014年(1)

2012年(5)

2011年(58)

2010年(56)

2009年(9)

我的朋友

分类: 嵌入式

2010-12-15 11:07:31

    在没有接触BOOTLOAD之前,看着别人搞这“玩意儿”,觉得是一个很有技术含量的事,当细细的去“品”过后,也就是那么一回事,作技术就是要深专,只要“钻”进去了,在某一时刻就会恍然大悟。今天就将我对BOOTLOAD升级过程的理解和大家分享分享。

    BOOTLOAD并不是只有arm中才有的,其它的嵌入式系统甚至PC上都会有bootloader,主要的作用就是引导操作系统。在硬件起动后,硬件设备尚未初始化,直接加载体积较大的系统比较困难,有时甚至无法加载,如系统内核在网络上的情况,所以常常在系统运行前,提供一个体积较小但又具体初始化基本软硬件环境的程序来运行,由它来载入系统并设置系统运行参数,并最终运行系统,这就是bootloader。

    它可以分为两大类,一类BOOTLOAD是芯片在出厂时,生产商固化在ROM中的BOOTLOAD;二类BOOTLOAD是用户在设计过程中,根据实际工程的需要设计一小段代码,使新的应用程序从非JATG接口引导到ROM区或RAM区。其实这两类在功能上都差不多。本文讨论二类BOOTLOAD,讨论以Cotex-M3内核的LM3S系列处理器为对象。代码和编译器是基于IAR5.11版本。

    我们都知道任何程序都是从复位开始执行起走的,BOOTLOAD也不例外。当芯片复位后(软、硬件复位,掉电复位等),系统产生复位中断,执行复位中断服务程序,复位中断的优先级最高,所以不需要考虑有谁可以打断它(专心做它自己的事)。当然,程序员就可以在复位中断里面打芯片的主意了。BOOTLOAD升级应用程序的点子就是从这里出发的(一发不可收拾“坏透了”),它先把自己的BOOTLOAD复制到RAM区,再零填充.bss段(数据段,主要用于存放那些初始化为0的变量和没有初始的自动变量。位于SRAM区)。然后就让向量表的偏移从RAM的起始地址开始算起,LM3Sxxxx是从0x2000 0000开始偏移,到此,开始在RAM区里执行。

    在RAM区里执行时,会对升级信号进行检测,如检测到要升级信号,则开始配置要升级的接口(升级前的准备),准备好了就升级应用程序。如果没有检测到升级信号则执行原有的用户应用程序。我们来看看启动代码分析,就知道是怎么回事了。
//*********************************************************
// 包含配置头文件
//*********************************************************
#include "bl_config.h"
//*********************************************************
// 声明.bbs数据段,此段位于RAM区
//*********************************************************
    rseg    .bss:DATA(2)
//*********************************************************
// 分配存储堆栈
//*********************************************************
g_pulStack ds8 STACK_SIZE * 4
//*********************************************************
// 声明INTVEC段,此段位于ROM区,用于存放下面这一段向量表
//*********************************************************
    rseg    INTVEC:CONST(2)
//*********************************************************
// Cortex-M3 处理器的简化向量表,也是必要的,
//*********************************************************
    export  __vector_table
__vector_table
    dcd     g_pulStack + (STACK_SIZE * 4)  // Offset 00: Initial stack pointer
    dcd     ResetISR - 0x20000000          // Offset 04: Reset handler
    dcd     NmiSR                          // Offset 08: NMI handler
    dcd     FaultISR                       // Offset 0C: Hard fault handler
    dcd     IntDefaultHandler              // Offset 10: MPU fault handler
    dcd     IntDefaultHandler              // Offset 14: Bus fault handler
    dcd     IntDefaultHandler              // Offset 18: Usage fault handler
    dcd     0                              // Offset 1C: Reserved
    dcd     0                              // Offset 20: Reserved
    dcd     0                              // Offset 24: Reserved
    dcd     0                              // Offset 28: Reserved
    dcd     UpdateHandler - 0x20000000     // Offset 2C: SVCall handler
    dcd     IntDefaultHandler              // Offset 30: Debug monitor handler
    dcd     0                              // Offset 34: Reserved
    dcd     IntDefaultHandler              // Offset 38: PendSV handler
#if defined(ENET_ENABLE_UPDATE)
    import  SysTickIntHandler
    dcd     SysTickIntHandler              // Offset 3C: SysTick handler
#else
    dcd     IntDefaultHandler              // Offset 3C: SysTick handler
#endif
#if defined(UART_ENABLE_UPDATE) && defined(UART_AUTOBAUD)
    import  GPIOIntHandler
    dcd     GPIOIntHandler                 // Offset 40: GPIO port A handler
#endif
//*********************************************************
// 声明代码段CODE,用于存放代码段
//*********************************************************
    rseg    CODE:CODE(2)
    thumb
//*********************************************************
// 初始化时,把flasn里面的boot loader拷贝到SRAM区,
// 零填充.bss段并从SRAM区向量表开始执行
//*********************************************************
ProcessorInit
    //
    // 把flash 的 boot loader拷贝到SRAM区
    //
    movs    r0, #0x00000000
    ldr     r1, =0x20000000         // SRAM区的起始地址
    ldr     r2, =SFB(.bss)
copy_loop
        ldr     r3, [r0], #4
        str     r3, [r1], #4
        cmp     r1, r2
        blt     copy_loop          // 复制boot loader代码
    //
    // 零填充.bss段
    //
    movs    r0, #0x00000000
    ldr     r2, =SFE(.bss)
zero_loop
        str     r0, [r1], #4
        cmp     r1, r2
        blt     zero_loop

    //
    // 设置中断向量表相对于SRAM区的起始地址的偏移量
    //
    ldr     r0, =0xe000ed08    // 中断向量的起始地址
    ldr     r1, =0x20000000
    str     r1, [r0]
  
    orr     lr, lr, #0x20000000 // 设置向量表偏移寄存器为SRAM底部

    bx      lr                 // 跳转到SRAM区执行

//*********************************************************
// 复位中断处理程序
//*********************************************************
    export  ResetISR        // 复位中断函数声明
ResetISR

    bl      ProcessorInit   // 调用ProcessorInit汇编代码

    //
    // 看是否有升级信号
    //
    import  CheckForceUpdate
    bl      CheckForceUpdate    // 调用升级检测程序
    cbz     r0, CallApplication // 如果检测到没有升级信号.就调用原有的应用程序。
                                // 如果有则继续向下执行

    //
    // 处理器相关配置
    //
#ifdef ENET_ENABLE_UPDATE       // 以太网升级接口配置
    import  ConfigureEnet
    bl      ConfigureEnet
#elif defined(CAN_ENABLE_UPDATE) // CAN接口升级配置
    import  ConfigureCAN
    bl      ConfigureCAN
#else
    import  ConfigureDevice     // 其它接口升级配置如I2C/UART/SSI
    bl      ConfigureDevice
#endif

    //
    //  调用升级程序
    //
#ifdef ENET_ENABLE_UPDATE        // 调用以太网升级程序
    import  UpdateBOOTP
    b       UpdateBOOTP
#elif defined(CAN_ENABLE_UPDATE) // 调用CAN升级程序
    import  UpdaterCAN
    b       UpdaterCAN
#else
    import  Updater              // 其它接口升级程序。开始升级处理了
    b       Updater
#endif

    //
    // 准备调用应用程序
    //
CallApplication                  // 如果没有升级信号就执行这里调应用程序
    ldr     r0, =APP_START_ADDRESS // 应用程序的起始地址加载到R0

    ldr     r1, =0xe000ed08     // 应用程序响量表的地址LM3S系列芯片中断向量是从0Xe000ed08开始的
    str     r0, [r1]

    // 从应用程序响量表中读出堆栈指针
    // 堆栈是向量表的一开始位址.LM3S芯片是堆栈的栈顶地址,有些芯片可能是栈底地址,
    // 如果是栈底的话,PC指针就等于SP+堆栈大小+4
    ldr     r1, [r0] 
    mov     sp, r1

    //
    // 从应用程序响量表中读出PC指针
    //
    ldr     r0, [r0, #4]
    bx      r0    // 开始执行应用程序

//*********************************************************
// 升级中断处理程序,这段代码和上面这段代码类似,不加分析。
//*********************************************************
UpdateHandler
    //
    // Initialize the processor.
    //
    bl      ProcessorInit

    //
    // Load the stack pointer from the vector table.
    //
    movs    r0, #0x00000000
    ldr     r0, [r0]
    mov     sp, r0

    //
    // Branch to the update handler.
    //
#ifdef ENET_ENABLE_UPDATE
    b       UpdateBOOTP
#elif defined(CAN_ENABLE_UPDATE)
    import  AppUpdaterCAN
    b       AppUpdaterCAN
#else
    b       Updater
#endif

//*********************************************************
// 不可屏蔽中断处理程序
//*********************************************************
NmiSR
    b       .     // 死循环
//*********************************************************
// 硬件错误中断处理程序
//*********************************************************
FaultISR
    b       .     // 死循环
//*********************************************************
// 未定义中断处理程序
//*********************************************************
IntDefaultHandler
    b       .     // 死循环
//*********************************************************
// 延时函数
//*********************************************************
    export  Delay
Delay
    subs    r0, #1
    bne     Delay
    bx      lr
//*********************************************************
// 启动文件结束
//*********************************************************
    end

阅读(2884) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~