ARM Boot Loader简介

1 Boot Loader 概述

简单地说,在操作系统内核运行之前,通过一小程序,可以初始化硬件设备、建立内存空间的映射图等,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核配置好相应的环境,也可以下载文件到系统板上的SDRAM ,对Flash 进行擦除与编程,这个小程序一般称为Boot Loader 。可以说,一个功能完善的Boot Loader 已经相当于一个微型的操作系统了。

Boot Loader 作为系统复位或上电后首先运行的代码,一般应写入Flash 存储器并从起始物理地址0x0 开始。Boot Loader 是非常依赖于硬件而实现的,而且根据实现的功能不同,其复杂程度也各不相同。一个简单的Boot Loader 可以只完成USB 口的初始化,而功能完善的Boot Loader 可以支持比较复杂的命令集,对系统的软硬件资源进行合理的配置与管理。因此,建立一个通用的Boot Loader 几乎是不可能的。

尽管Boot Loader 的功能各不相同,我们仍然可以对Boot Loader 归纳出一些通用的概念来,以指导用户对特定要求的Boot Loader 设计与实现。Boot Loader 一般应包括系统初始化过程,然后用户根据其系统的自身需求,具体设计。

系统初始化代码直接对ARM 微处理器内核及硬件控制器编程,多采用汇编语言编程,初始化代码一般应包括如下典型任务:

1.       定义程序入口点;

2.       设置异常和中断向量表;

3.       初始化存储设备;

4.       初始化堆栈指针寄存器;

5.       初始化用户执行环境;

6.       呼叫主应用程序。

1.1 定义程序入口

    初始化代码必须定义整个程序的入口点。通过伪指令Entry 指定编译器保留该段代码,同时配合链接器的设置,确定整个程序的入口点。

1.2 设置异常和中断向量表

异常和中断处理是嵌入式系统的重要核心部分。它们负责处理错误、中断和其它由外部系统触发的事件。

ARM 要求异常向量表必须放置在从0 地址开始,连续32 (8X4 )字节的空间内,各异常的位置如表1 所示。


每当一个异常发生以后, 系统执行完当前指令后,ARM 微处理器会执行以下几步操作:

(1)          把cpsr 保存到相应异常模式下的spsr ;

(2)          把pc 保存到相应异常模式下的lr ;

(3)          设置cpsr 为相应的异常模式(异常及其对应的模式见图1 );


 

(4)          设置pc 为相应异常处理程序的入口地址。

 

因为每个异常只占据向量表中1 个字的存储空间,只能放置一条ARM 指令,所以该指令必须使程序跳转到存储器存储异常处理程序的地方,再执行异常处理程序。

执行完异常处理程序,要从异常中断处理程序中返回。

从异常中断处理程序中返回包括下面两个基本操作:

(1)          恢复被屏蔽的程序的处理器状态

(2)          返回到发生异常中断的指令的下一条指令处继续执行。

当异常发生时,程序计数器PC 所指的位置对于各种不同的异常是不同的,同样,返回地址对于各种不同的异常中断也是不同的。例外的是,复位异常中断处理程序不需要返回,因为整个应用系统是从复位异常中断处理程序开始执行的。

总 的来说,异常处理过程为:当异常发生时,系统执行完当前指令后,将跳转到相应的异常处理程序处执行。当异常处理程序执行完成后,程序返回到发生异常的指令 的下一条指令处执行。在进入异常处理程序时,要保存被中断的程序的执行现场。从异常处理程序退出时,要恢复被中断的程序的执行现场。

 

IRQ 和FIQ 异常是用来处理外围设备中断的,不同的ARM 芯片生产商,生产的芯片会有所不同。

1.3 初始化存储设备

初始化存储器系统主要是对系统存储器控制器的初始化,如果系统具有存储器管理单元,也必须对其进行初始化。

ARM 微处理器架构的理论寻址能力为4GB ,但是对于一个特定的系统来说,所配备的实际物理存储器远没有那么多。出于对芯片的面积、成本、使用灵活性等方面的考虑,这么大容量的存储器如果设计在芯片内部是不能被接受的。因此,基于ARM 微处理器的系统一般都需要外扩大容量的存储器,这些存储器是由专门的存储器控制器控制的。

因为存储器控制器并非标准ARM 微处理器架构的一部分,所以不同的ARM 微处理器,设计也会各不相同,初始化代码自然也会有一些差异,但一般包含如下两个方面:

1.       存储器类型和时序的配置

ARM 微处理器一般都设计有多种类型的存储器接口,需要根据具体的系统设计加以正确配置,且对于同在一种类型的存储器,也会因为访问速度的差异进行不同的时序设置。

存 储器接口时序优化是非常重要的,这会影响到整个系统的性能。因为一般系统运行的速度瓶颈都存在于存储器访问,所以存储器访问时序应尽可能的快;而同时又要 考虑到由此带来的稳定性问题,应根据不同的情况来配置。一般一个特定的系统,需要进行多次测试,才能确定最佳的时序配置。

2.       存储器的地址分配与地址重映射

ARM 微处理器架构的理论寻址能力为4GB ,但是对于一个特定的系统来说,所配备的实际物理存储器远没有那么多。因此,存储器的地址分配也就是将物理存储器定位在4GB 地址空间的哪个具体位置。

    ARM 微处理器通常采用两种方式来完成地址分配:一种方式采用固定的存储器地址分配,即物理存储器的起始地址是固定的;另一种方式采用存储器重映射的手段,使物理存储器的起始地址可以在有效的地址空间内任意改变,即物理存储器的地址是不固定的。

    基于固定存储器地址分配的ARM 微处理器系统,当系统的硬件设计完成以后,物理存储器地址也就确定了,在软件设计的程序流程中考虑就简单一些。而对于具有存储器地址重映射功能的系统,这个过程就复杂的多。

    一种典型的存储器地址重映射过程描述如下:当系统上电或复位以后,PC 指针指向0x0 ,程序从0x0 地址开始执行,因此,为了能正确读取代码,要求此时Flash (或其它类型的ROM )的起始地址为0x0 。但Flash (或其它类型的ROM )的访问速度大大低于RAM ,每次产生异常后,都要从Flash (或其它类型的ROM )的异常向量表 调转到相应的处理程序,会影响异常的响应速度,因此,系统便提供一种灵活的地址重映射方法,在系统完成必要地初始化以后,将RAM 安排到0x0 地址处,而将原来位于0x0 处的Flash (或其它类型的ROM )安排到其他的地方上去,加快异常的响应速度。

    这个过程中最容易出错的地方是如何保证程序执行流程的连续性。因为PC 指针最初在Flash 里取指令执行,在进行地址重映射以后,Flash (或其它类型的ROM )被安排到其他地址上去了,而当前地址被安排为RAM ,如果事先没有对RAM 的内容进行正确地设置,在往下取指令执行就会出错,即程序的连续性被存储器地址重映射这种变化所打断。

    常用的处理方法是:先将Flash (或其它类型的ROM )的内容全部复制到RAM 中,然后再进行地址重映射。此时尽管Flash (或其它类型的ROM )和RAM 的物理地址发生了变化,但由于RAM 中的内容与原来的Flash (或其它类型的ROM )是一样的,PC 指针就可以继续取得正确地指令执行,从而保证了程序流程的连续性。

1.4 初始化堆栈

因为ARM 有7 种执行状态,每一种状态的堆栈指针寄存器(SP )都是独立的(注意System 和User 模式使用同一个SP )。所以,对程序中需要用到的每一种模式都要给SP 定义一个堆栈地址。方法是改变状态寄存器内的状态位,使处理器切换到不同的状态,然后给SP 赋值。注意:不要切换到User 模式进行User 模式的堆栈设置,因为进入User 模式后就不能再操作CPSR 回到别的模式了,可能会对接下去的程序执行造成影响。

1.5 初始化用户执行环境

主要包括初始化临界I/O 设备,初始化应用程序执行环境,改变处理器的运行模式和状态,使能中断4 部分。

初始化临界I/O 设备。临界I/O 设备是指哪些使能中断之前必须进行初始化的设备。如果不对这些设备进行必要的初始化,它们可能会在使能中断后产生一些没有意义的中断请求,从而影响程序的运行。

初始化应用程序执行环境。程序代码通过编译、链接后生成可执行映像文件,一个ARM 映像文件由RO ,RW 和ZI 三个段组成,其中RO 为代码段,RW 是已初始化的全局变量,ZI 是未初始化的全局变量。映像一开始总是存储在ROM /Flash 里面的,其RO 部分即可以在ROM /Flash 里面执行,也可以转移到速度更快的RAM 中执行;而RW 和ZI 这两部分是必须转移到可写的RAM 里去。所谓应用程序执行环境的初始化,就是完成必要的从ROM 到RAM 的数据传输和内容清零。

改变处理器的运行模式和状态。ARM 微处理器在复位或上电状态下的默认模式为系统管理模式,而在初始化代码中可能需要切换到其它模式进行必要的操作,如初始化各个模式下的堆栈指针寄存器。因此,在系统的初始化过程中处理器模式一般会经历如图2 所示的变化。同时,ARM 微处理器在复位后总是处于ARM 状态,对于兼容Thumb 指令的处理器如果应用程序的入口点对应Thumb 指令,则必须将微处理器切换到Thumb 状态。


使能中断。如果系统需要使用中断,初始化代码应该使能中断。中断使能可以通过清除CPRS 中的中断禁止位来完成。

1.6 呼叫主应用程序

当所有的系统初始化工作完成之后,就需要把程序流程转入主应用程序。如果主应用程序是由C 代码编写,可以通过以下两种方式进入C 代码运行,最简单的情况如下:

 

IMPORT         C_Entry       ;定义一个外部标号,最好不使用main

B              C_Entry       ;跳转到该处执行

 

在ARM 的ADS 编译环境中,还另外提供了一种进入C 代码的机制:

 

IMPORT         __main

B              __main

 

__main() 是编译器提供的一个函数,负责完成库函数的初始化和对C 代码运行环境的初始化,最后自动调转到main() 函数执行,此时应用程序的主函数名必须是main() 。

用户可以根据需要选择是否使用main() 函数,如果想让系统自动完成初始化过程,可以使用main() 函数;如果所有的初始化过程都由用户自己完成,则不使用main() 。
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值