最近查了很多关于基于ARM嵌入式系统上电复位后启动过程的资料。搞得晕晕乎乎,不知如何理解。现在终于有一点头绪,总结如下:
我主要遇到了这样一个问题,启动代码和Bootloader的区别以及它们在ARM上运行操作系统和不运行操作系统(只有用户程序)的关系。
启动代码和Bootloader的关系类似于“男人”和“人类”的关系。可以说男人是人类的一种,但不能说人类只有男人。也就是说可以说启动代码是Bootloader,但不能说Bootloader是启动代码。当然这也不是绝对的,只在某些情况下可以这么说。
启动代码是指CPU复位后到main函数之前需要执行的汇编代码。需要这段代码是为了给C语言程序准备好堆栈空间,中断入口和外部数据等。而且这段汇编代码可以直接对硬件进行操作,效率很高。
Bootloader就是在操作系统内核运行之前的一小段程序,通过这段小程序,可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为调用操作系统内核、运行用户应用程序准备好正确的环境。嵌入式系统建立一个通用、标准的Bootloader是非常困难的。Bootloader也依赖于具体的嵌入式板级设备的配置,这也就是说,对于两块不同的嵌入式主板而言,即使它们是基于同一 CPU 而构建,要想让运行在一块板子上的 Bootloader 程序也能运行在另一块板子上,通常都需要修改
Bootloader 的源程序。
启动流程:系统加电复位后,几乎所有的 CPU都从由复位地址上取指令。以微处理器为核心的嵌入式系统通常都有某种类型的固态存储设备(比如EEPROM、FLASH等)被映射到这个预先设置好的地址上。通过集成开发环境可以将Bootloader定位在复位地址开始的存储空间内,因此Bootloader是系统加电后、操作系统内核或用户应用程序运行之前,首先必须运行的一段程序代码。系统的启动通常有两种方式,一种是可以直接从Flash启动,另一种是可以将压缩的内存映像文件从Flash(为节省Flash资源、提高速度)中复制、解压到RAM,再从RAM启动。当电源打开时,一般的系统会去执行ROM(应用较多的是Flash)里面的启动代码。系统启动代码完成基本软硬件环境初始化后,对于有操作系统的情况下,启动操作系统、启动内存管理、任务调度、加载驱动程序等,最后执行应用程序或等待用户命令;对于没有操作系统的系统直接执行应用程序或等待用户命令。
启动代码内容流程:
1. 启动代码的第一步是设置中断和异常向量。
2. 完成系统启动所必须的最小配置,某些处理器芯片包含一个或几个全局寄存器,这些寄存器必须在系统启动的最初进行配置。
3. 设置看门狗,用户设计的部分外围电路如果必须在系统启动时初始化,就可以放在这一步。
4. 配置系统所使用的存储器,包括Flash,SRAM和DRAM等,并为他们分配地址空间。如果系统使用了DRAM或其它外设,就需要设置相关的寄存器,以确定其刷新频率,数据总线宽度等信息,初始化存储器系统。有些芯片可通过寄存器编程初始化存储器系统,而对于较复杂系统通常集成有MMU来管理内存空间。
5. 为处理器的每个工作模式设置栈指针,ARM处理器有多种工作模式,每种工作模式都需要设置单独的栈空间。
6. 变量初始化,这里的变量指的是在软件中定义的已经赋好初值的全局变量,启动过程中需要将这部分变量从只读区域,也就是Flash拷贝到读写区域中,因为这部分变量的值在软件运行时有可能重新赋值。还有一种变量不需要处理,就是已经赋好初值的静态全局变量,这部分变量在软件运行过程中不会改变,因此可以直接固化在只读的Flash或EEPROM中。
7. 数据区准备,对于软件中所有未赋初值的全局变量,启动过程中需要将这部分变量所在区域全部清零。
8. 最后一步是调用高级语言入口函数,比如main函数等。