×
嵌入式 > 嵌入式开发 > 详情

ARM7嵌入式系统中Bootloader分析与设计

发布时间:2020-08-26 发布时间:
|
1.引言

作为一种16/32位的高性能、低成本、低功耗嵌入式RISC(Reduced Instruction Set Computer)微处理器,ARM(Advanced RISC Machines )微处理器目前已经成为应用最为广泛的嵌入式微处理器[1]。在嵌入式系统开发中Bootloader常常是嵌入式系统开发中可能遇到的第一个技术难点。应用程序运行环境能否正确构建,内核能否启动成功,都取决于Bootloader能否正确的工作。一个功能完善的嵌入式系统Bootloader还要求能够提供系统更新的能力,以及为了实现这一操作所需要的一个简单的命令控制台。本文在基于ARM7-uClinux嵌入式系统的硬件平台和软件平台基础上,描述了系统引导程序Bootloader的设计原理,阐述了设计时应考虑的因素和需解决的技术难点并给出了一套可行的引导程序流程。

2.系统组成

典型的ARM嵌入式系统硬件平台一般包括一个以ARM为内核的处理器、存储器和必要的外部接口与设备。在本系统中采用内嵌ARM7TDMI的Samsung公司S3C4510B处理器,存储器使用2MB的Flash和16MB的SDRAM,外部接口除了可用于下载和通信的串口,还配备了一个以太网接口以支持S3C4510B的网络功能。

软件平台由系统引导程序、嵌入式操作系统内核和文件系统组成,系统引导程序通常即指我们这里的Bootloader,其代码量虽少但是作用非常大,相当于普通PC机中的BIOS。通过Bootloader,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。

3.Bootloader 设计分析

3.1 Bootloader 的操作模式 (Operation Mode)

大多数 Bootloader 都包含两种不同的操作模式[2]:

(1). 启动加载(Boot loading)模式:也称为“自主”模式。即Bootloader 从目标机上的某个固态存储设备上将操作系统加载到 RAM 中运行,整个过程并没有用户的介入。

(2).下载(Downloading)模式:在这种模式下,目标机上的Bootloader将通过串口或网络连接等通信手段从主机(Host)下载内核映像和根文件系统映像等,然后保存到目标机上的FLASH 类固态存储设备中。Bootloader的这种模式通常在系统初次安装和更新时被使用,工作于这种模式下的Bootloader通常都会向它的终端用户提供一个简单的命令行接口。

在我们的Bootloader设计中我们同时支持这两种工作模式,采用的方法是:一开始启动时处于正常的启动加载模式,但并不立即启动进入uClinux内核,而是提示延时5秒,等待终端用户如果按下某一特定按键,则切换到下载模式,否则继续启动uCLinux 内核。

3.2 Bootloader 的启动及初始化

基于ARM的芯片多数为复杂的片上系统(SoC),这类复杂系统里的多数硬件模块都是可配置的[3]。因此大多数 Bootloader 都分为 stage1 和 stage2 两大部分。依赖于 CPU 体系结构的代码,通常都放在 stage1 中,而且在这一部分,我们直接对处理器内核和硬件控制器进行编程,因此常常都用汇编语言来实现。而stage2则通常用C语言来实现,这样可以实现更复杂的功能,而且代码会具有更好的可读性和可移植性。

3.2.1 Bootloader的stage1

这部分代码必须首先完成一些基本的硬件初始化,为stage2的执行以及随后的kernel 的执行准备好一些基本的硬件环境[2]。Bootloader的stage1一般通用的内容包括:

* 定义程序入口点

* 设置异常向量表

* 初始化存储系统(包括地址重映射)

* 初始化有特殊要求的端口,设备

* 初始化用户程序的执行环境

* 初始化堆栈指针寄存器,必要时改变处理器的模式

* 设置FIQ/IRQ中断处理程序入口

* 进入C程序

在整个Bootloader的初始化过程中我们都不必响应中断,因此首先禁止系统的中断,然后程序设置CPU的速度和时钟频率,设置CPU内部指令/数据cache,DRAM初始化,DRAM初始化完成后即可拷贝ROM中的代码到DRAM中,然后内存重映射,程序开始进入DRAM中执行,然后再初始化一些用户有特殊要求的端口、设备,比如LED或串口等,可以通过点亮LED,或者向串口打印一些调试信息,以此表明系统的状态是OK还是Error。然后准备进入C语言代码:拷贝Bootloader的RW/RO 段到相应的运行位置,初始化ZI段,初始化系统堆栈,设置FIQ/IRQ中断处理程序入口,设置完成就可以进入到C代码了。

3.2.2 Bootloader的stage2

为了让程序跳入C语言的“main”函数,我们采用直接将pc指针指向“main”函数的方法,实现代码如下:

[ THUMBCODE ; [ = IF , 如果是汇编Thumb代码,则采用bx指令跳转

bx lr

| ; | = ELSE

mov pc, lr ; 汇编ARM代码,则直接跳转到main函数

] ; ] = ENDIF

进入main函数后即可以开始本阶段stage2的初始化任务,这包括:

(1)初始化至少一个串口,以便和终端用户进行交互;

(2)初始化计时器,延时并提示启动模式的选择,如果进入启动加载模式,则系统控制权交由uClinux操作系统,Bootloader功成身退,否则程序继续向下执行。

(3)初始化网络,包括网络基本信息配置等;

(5)如果系统配有LCD等外设,可以在此初始化;

(6)初始化Flash:检测是否支持该Flash芯片(可通过比较Flash ID的方式实现);

(7)初始化中断,包括屏蔽中断,清除中断悬挂标志,初始化中断向量表,注册需要的中断处理函数等。

(8)初始化命令控制台,等待用户键入命令。

在初始化这些设备之前,也可以改变 LED 灯的状态,以表明我们已经进入main函数执行。设备初始化完成后,可以通过串口输出一些打印信息,如程序名字字符串、版本号等。本系统中采用的系统启动引导方案流程图如图1所示。

4.难点分析

4.1异常及中断处理

在ARM支持的7种模式中,共有5种异常模式,而其中又尤以外部中断模式(IRQ)应用较为广泛,其异常处理过程也较为复杂。本文下面将以IRQ异常处理为例,讲述一个通用的中断使用及处理过程。一个ARM通用的中断处理过程大致可以分为以下3步:

(1) 异常响应:获取异常处理程序入口地址,并进入异常处理程序;

(2) 现场保护及恢复:即进入中断服务程序(ISR)前后中断现场的保护和恢复;

(3) 中断服务:计算中断源索引号,清中断,然后进入中断服务。

本例中IRQ异常处理相关代码如下:

ResetEntry

b SYS_RST_HANDLER ;复位异常

......

b IRQ_SVC_HANDLER ;外部中断请求

MACRO ;通过一个宏定义,统一处理各异常处理程序与异常向量地址的映射关系

$HandlerLabel HANDLER $ExceptHandler

$HandlerLabel

sub sp,sp,#4 ;预留一个字的空间用来保存PC的跳转地址

stmfd sp!,{r0} ;保存下面中断处理中使用到的r0寄存器

ldr r0,=$ExceptHandler ;将保存有异常处理函数入口的地址读入r0

ldr r0,[r0] ;将异常处理函数入口读入r0

str r0,[sp,#4] ;将异常处理函数入口存入堆栈中刚才预留的空间

ldmfd sp!,{r0,pc} ;恢复现场,同时跳入异常处理函数

MEND

IRQ_SVC_HANDLER HANDLER IrqSvcVector ;调用宏定义

......

SYS_RST_HANDLER ;复位异常时程序跳转地址

...... ;系统初始化代码

ldr r0, =IrqSvcVector

ldr r1, =IRQ_SERVICE

str r1, [r0] ;将IRQ异常处理程序入口存入变量IrqSvcVector

......

IRQ_SERVICE

STMFD sp!, {r0-r12, lr}

BL ISR_IrqHandler ;跳入C语言中定义的中断服务程序(ISR)

LDMFD sp!, {r0-r12, lr}

SUBS pc, lr, #4 ;异常返回同时复制相应SPSR到CPSR,实现处理器模式自动切换

void ISR_IrqHandler(void) //C语言中定义的中断服务程序

{

unsigned int IntOffSet;

IntOffSet = (unsigned int)inl(INTOFFSET);//取出中断源索引号

Clear_PendingBit(IntOffSet>>2); //清中断

(*InterruptHandlers[IntOffSet>>2])(); // 进入对应的中断服务子函数

}

从上面的代码我们可以总结得出,接收到IRQ中断请求后程序的执行流程是:

(1)执行完当前指令,程序自动跳到0x18地址;

(2)从0x18 程序跳转到IRQ_SVC_HANDLER;

(3)从IRQ_SVC_HANDLER 再到SDRAM高端异常矢量表;

(4)从SDRAM高端异常矢量表跳转到IRQ_SERVICE异常处理程序;

(5)由IRQ_SERVICE最后进入中断服务程序,完成中断处理任务后返回。

4.2 命令控制台

当Bootloader工作在下载模式时,通常会通过串口向终端用户提供一个简单的命令控制台,为了使用的方便,我们这里对其功能进行了扩充,添加了命令键入时对键盘“上、下、左、右、Home、End”几个方向键的支持。

通过串口收发或显示字符时,我们使用的通常是字符的ASCII码。对于非控制字符,也即键入命令时我们可能使用到的命令字符,在控制台中我们使用了ASCII码值从0x20~0x7e之间的字符。对于控制字符,在常用字符ASCII码对照表中我们可以找到Enter键、Backspace(退格)键以及ESC键的ASCII码,但是却没有上下左右方向键以及Home、End键对应的ASCII码,通过对键盘输入字符的串口收发测试发现,如果在测试时按下了方向键则串口在每次按键后会连续发送出3字节数据,前两字节所有方向键的数据相同,分别是0x1b,0x5b,第3字节对应不同的按键,上下右左方向键分别对应的值为:0x41、0x42、0x43、0x44,Home和End键对应的值为0x48和0x4b。故要检测键盘是否键入了方向键,需要向串口连续读取三字节的数据,同理,要控制光标向左、向右移动或Home、End也需要连续一次向串口发送3字节数据。命令控制台从串口接收到字符后,程序处理的流程图如图2所示。

5.结束语

Bootloader与具体的硬件环境和操作系统是紧密联系在一起,针对某个CPU芯片编写Bootloader代码,首先要了解该CPU的内核结构、指令系统,其次是具体芯片的结构和各种片上资源,以及所采用的操作系统。本文给出的Bootloader代码已经在基于Samsung公司的S3C4510B芯片开发的系统板上运行并测试通过。该Bootloader能够正常引导及更新uClinux内核,系统运行稳定,完全实现了设计目的,达到了嵌入式系统的设计要求。





『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
Recogni:将高端AI芯片推向自动驾驶边缘