嵌入式系统实验报告

嵌入式系统实验报告

学 院 测量与通信工程学院

专 业 信号与信息处理

学生姓名 姜 元

学 号 1320600050

指导教师 董静薇

一、 实验目的

?

?

?

?

了解BootLoader的基本概念和框架结构 了解BootLoader引导操作系统的过程 掌握bootloader程序的编译方法 掌握BootLoader程序的使用方法

二、 实验内容

?

?

?

?

?

?

熟悉嵌入式linux环境下的开发工具,包括make、gcc、超级终端等 bootLoader程序的编译和下载 内核和文件系统的编译和下载 实验分析bootLoader程序的实现原理和结构 观察程序运行情况 完成实验手册题2和题5

三、 实验原理

1. BootLoader的功能与结构

1) 嵌入式Linux系统软件结构如图1所示:

嵌入式系统实验报告

图1 嵌入式Linux系统软件结构

2) 什么是BootLoader?

简单地说,BootLoader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。

由于硬件结构差别较大,不同的目标板需要不同的BootLoader。

3) BootLoader 的主要功能

? 初始化系统在启动阶段必需的硬件设备;

? 准备后续软件系统(如操作系统)运行所需的软件环境,如复制操作系统内核代码到RAM中等;

- 1 -

?

?

?

? 向内核传递启动参数; [可选] 配置系统各种参数; [可选] 支持各种协议来下载BootLoader、内核、文件系统等; [可选] 在线烧写系统firmware(固件),如启动参数、BootLoader、内核、文件系

统等;

? [可选] 支持在线调试;

? 引导内核启动。

4) 固态存储设备的空间分配

在固态存储设备中的典型空间分配情况如图2所示:

起启动参数始 地 址

Bootloader

图2 固态存储设备中的典型空间分配情况 结

嵌入式系统实验报告

5) 典型BootLoader的启动流程

典型的BootLoader启动流程如图3所示

嵌入式系统实验报告

- 2 -

嵌入式系统实验报告

图3 典型的BootLoader启动流程

6) BootLoader的工作模式

功能完善的BootLoader一般包含2种工作模式:

? 启动加载模式(loading)

从最终用户的角度看,BootLoader的功能就是加载操作系统。

? 下载更新模式(downloading)

主要用于嵌入式系统开发人员。

启动加载模式(loading)

? 这种模式也称为“自主”(Autonomous)模式;

? BootLoader从目标机上的某个固态存储设备上将操作系统加载到RAM中运行,整

个过程并没有用户的介入;

? BootLoader的正常工作模式,在嵌入式产品发布的时侯,BootLoader 必须工作在该

模式下。

下载更新(Downloading)模式

? 该模式下,目标机上的 BootLoader将通过串口或USB、网络连接等从主机(Host)

下载文件,比如:下载内核映像和根文件系统映像等。

? 下载的文件通常被BootLoader保存到目标机的 RAM 中,然后再被写到目标机上的

FLASH 类固态存储设备中。

? 该模式在第一次安装内核与根文件系统时会被使用;系统更新也会用到。

? 该模式下的 BootLoader通常都会为用户提供一个简单的命令行接口。

此次实验过程中即使用下载模式。

嵌入式系统实验报告

- 3 -

7) BootLoader的典型结构框架

嵌入式系统实验报告

大多数Bootloader的执行分为两个阶段:

a) 第一阶段/也称FlashLoader

--运行于Flash的一段BootLoader代码

特点:

? 代码量小,往往不会超过256B;

? 汇编写成;

? 运行于Flash;

? 由于加载BootLoader后,需要运行BootLoader,因此最后往往是一条跳转语句。

主要操作步骤:

? 硬件设备初始化。

? 为加载BootLoader准备RAM空间。

? 拷贝BootLoader到RAM空间中。

? 设置好堆栈。

? 跳转到BootLoader的C语言入口点。

b) 第二阶段/也称BootLoader

--运行于RAM中的BootLoader主要代码

特点:

BootLoader是操作系统内核运行前的核心程序,它具有如下特点:

? 代码量大;

? 由C语言写成,大多数时候需要嵌入式汇编语言;

? 运行于SDRAM等随机存储器

? 由于它是启动内核前运行的最后一个程序,它必须把控制权交给内核,因此它最后

是一条跳转到系统内核的语句。

主要操作步骤:

? 初始化本阶段要使用到的硬件设备。

? 检测系统内存映射(memory map)。

? 将kernel映像和根文件系统映像从flash上读到RAM空间中。

? 为内核设置启动参数。

? 启动内核。

8) PXA270开发板Flash和SDRAM空间分配

? Defined in xsbase270.h (bootloader源代码中)

? #define LOADER_SRAM_BASE 0x00000000

? #define LOADER_DRAM_BASE 0xA3F00000

? #define LOADER_MAX_SIZE 0x00040000

- 4 -

?

?

?

?

?

?

?

?

?

#define KERNEL_SRAM_BASE 0x00040000 #define KERNEL_DRAM_BASE 0xA0008000 #define KERNEL_MAX_SIZE 0x00140000 #define RAMDISK_SRAM_BASE 0x00180000 #define RAMDISK_DRAM_BASE 0xA1000000 #define RAMDISK_MAX_SIZE 0x00300000 #define ROOTFS_SRAM_BASE 0x00180000 #define ROOTFS_DRAM_BASE 0xA0000000 #define ROOTFS_MAX_SIZE 0x01e80000

2. 嵌入式Linux内核的启动过程

嵌入式LINUX内核的版本控制:

内核版本号格式为:“X . YY . ZZ”,如2.4.18。

--X:主版本号,介于0和9之间,它的变化意味着内核在设计或实现上有重大变化。 --YY:次版本号,介于0和99之间,它一方面表示版本的变迁,一方面表示版本类型,即“发行版”或“开发版”。YY为偶数则表示相对稳定的发行版,而奇数则表示还在开发中的版本,它可能不太稳定,运行中可能会出现较大的问题。

--ZZ:表示内核经过较小修改后的版本。

嵌入式Linux的版本号后面还会加一个后缀,如“rmk4-mx1bsp0.3.6”,该后缀往往表示针对某个开发平台的补丁。几个常用的后缀:

--rmk:表示由Russell King维护的ARM Linux;

--np:表示由Nicolas Pitre维护的基于StrongARM和Xscale的ARM Linux;

--ac:表示由Alan Cox(Alan Cox是仅次于Linus的Linux维护人员,主要负责网络部分和OSS等的维护工作)维护的Linux代码;

--hh:表示由网站发布的ARM Linux代码,主要是基于Xscale的,它包括工具链、内核补丁、嵌入式图形系统等。

嵌入式LINUX的内核源代码结构:

? COPYING

– GPL版权申明。

? CREDITS

– 光荣榜。对Linux做出过重大贡献的人员信息。

? MAINTAINERS

– 维护人员列表,对当前版本的内核各部分都有谁负责;

? ReadMe

– Linux内核安装、编译、配置方法等的简单介绍;

? Makefile

– 第一个Makefile文件。用来组织内核的各模块,记录各模块间的相互联系和

依托关系,编译时使用。

? Rules.make

– 各种Makefile以及make所使用的一些共同规则;

- 5 -

? REPORTING-BUGS

– 有关报告Bug的一些内容;

? Documentation/

– 关于内核源代码的一套非常有用的文档;

? Arch/

所有和体系结构相关的核心代码,每个子目录对应一种CPU体系结构;

– i386 IBM的PC体系结构

– mips SGI的MIPS体系结构

– m68k Motorola的基于C680x0的体系结构

– arm 基于ARM处理器的体系结构

– Sparc sun的sparc处理器体系结构

– 等等

? Include/

– 包括编译核心所需要的大部分头文件。

– 与平台无关的头文件在include/linux子目录下;

– 与ARM CPU相关的头文件在include/asm-arm子目录下;

– include/scsi目录则是有关scsi设备的头文件目录;

? Init/

– 内核的初始化代码, 包括两个重要文件main.c和Version.c,这是研究核心

如何工作的好的起点之一。

? Mm/

– 所有独立于cpu体系结构的内存管理代码,如页式存储管理内存的分配和释

放等;

– 和体系结构相关的内存管理代码则位于arch/*/mm/,例如arch/arm/mm/; ? Kernel/

– 主要的内核代码,此目录下的文件实现了大多数linux系统的内核函数; – 其中最重要的文件为sched.c;

– 和体系结构相关的代码在arch/*/kernel中;

? Drivers/

设备驱动程序;每种驱动程序又各占用一个子目录

– Block: 块设备驱动程序

– Scsi: SCSI设备驱动程序

– Char: 字符设备驱动程序

– net 网卡设备

– sound 音频卡设备

– video 视频卡设备

– cdrom 专用CD-ROM设备(除ATAPI和SCSI之外)

– isdn ISDN设备

– sgi SGI的设备

– acorn Acorn的设备

– pnp 即插即用的支持

– usb 通用串行总线(USB)的支持

– pci PCI总线的支持

– sbus Sun的SPARC SBus的支持

- 6 -

– nubus 苹果的Macintosh Nubus的支持

? Fs/

– 所有的文件系统代码和各种类型的文件操作代码,它的每一个子目录支持一个文件

系统,例如fat和ext2;

? Ipc/

– 包含核心的进程间通信的代码;

? Lib/

– 放置核心的库代码;

? Net/

– 核心与网络相关的代码;

? Modules/

– 模块文件目录,一般为空目录,用于存放编译时产生的模块目标文件;

? Scripts/

– 用于系统配置的命令文件,是一个脚本,用于对核心的配置;

嵌入式LINUX的内核启动过程:

Bootloader运行结束后,程序运行控制权交给操作系统内核,这时进入Linux内核启动阶段,该部分大致可以分为3个阶段:

? 第一阶段

? 主要是进行cpu和体系结构的检查、cpu本身的初始化以及页表的建立等; ? 第二阶段

? 主要是对系统中的一些基础设施进行初始化;

? 第三阶段

? 更高层次的初始化,如根设备和外部设备的初始化。

内核启动第1阶段:

从汇编文件linux/arch/arm/kernle/head-armv.S的ENTRY(stext)点处开始执行,直到start_kernel()函数,主要功能:

? 判断cpu类型

? 判断体系类型

? 创建核心页表

? 初始化处理器,打开MMU、CACHE等

? 跳转到init\main.c中的start_kernel函数处,开始执行c代码

内核启动第1阶段流程如图4所示:

- 7 -

图4 内核启动第1阶段流程

内核启动第2阶段:

从C代码文件linux/init/main.c中的start_kernel函数处开始执行,

??

直到调用kernelthread()产生init线程(位于linux/init/main.c),完成KERNEL的启动过程。

? 系统首先完成一些相关的初始化工作:

? lock_kernel();

? printk(linux_banner); //打印LINUX版本信息等。

? setup_arch(&command_line);//处理器相关的初始化过程

? printk("Kernel command line: %s\n", saved_command_line);

? parse_options(command_line); //解析BootLoader传递过来的内核参数

? trap_init();//设置陷阱门和中断门

? init_IRQ();//初始化系统IRQ

? sched_init(); //进程调度机制的初始化

…….

? console_init(); //控制台设备的初始化

? fork_init(mempages);// 根据系统物理内存的大小计算运行创建线程(包括进

程)的数量。

……

? 最后,在rest_init()中调用kernel_thread()产生产生第一个核心线程init(位于

linux/init/main.c),然后调用cpu_idle(),等待系统调度init线程,从而完成KERNEL的启动过程。

内核启动第3阶段:

内核启动第二阶段完成后,一般会在/sbin、/etc、/bin等目录下查找init程序并执行,init的执行过程如下:

? init从配置文件/etc/inittab中获取所有信息;

嵌入式系统实验报告

- 8 -

? /etc/inittab中会定义一个启动教本,如sysinit、rc、rcS等;

? PXA270开发系统中启动运行脚本文件/etc/rc.d/rc.sysinit,将完成驱动模块的动态加

载、配置网络等工作。

四、 实验步骤

1、阅读“Linux实验上机指导书”中有关bootloader的内容,以及课件相关内容,熟悉bootloader源代码的框架结构;

2、实验箱连线,包括仿真器、串口线、电源等;

3、打开一个Dos窗口,(开始->运行:输入“cmd”命令,然后回车);

4、利用telnet登陆到服务器:在dos窗口中输入218.192.169.240或者218.192.169.241; 用户名和密码同以前的实验。

5、现在你已经登陆到嵌入式linux开发环境服务器,也就是你面对的是一个linux操作系统环境。转换到/home/emb目录下(命令:cd /home/emb )

6、在当前目录下创建一个自己的目录,例如tmptest;命令:mkdir tmptest

7、利用leapftp软件将bootloader源代码(Boot-XSBase270_010005.tar.gz)上传到自己的目录下

8、输入输入tar xzvf Boot-XSBase270_010005.tar.gz,将源代码解压

9、输入“make” 编译bootloader程序,成功的话则生成文件:boot,则就是bootloader的可执行程序,将其下载到本地计算机待用。此时你可以选择根据Linux实验上机指导书的相关内容,编译内核和文件系统。也可以利用leapftp从课件中的实验参考资料的Image目录中直接下载内核(zImage_e24qt)和文件系统(rootfs270qt_24.img)到本地待用。

10、利用FlashWrite将boot文件烧写到Flash

11、打开程序->附件->通讯工具->超级终端,根据电脑的实际接口选择Com1

或com2,端口配置为:Rate:115200;Data Bits:8;Parity:无;Stop Bits:1;Flow Control:无

12、超级终端用于开发板上的嵌入式linux系统的信息界面

13、打开C:\Cisco TFTP Server\TFTPServer.exe软件,用于通过网线烧写内核

和文件系统。

14、此时重启开发板电源,烧写到开发板boot将开始运行,按“空格键”进入

bootloader的下载模式,界面如下:

- 9 -

15、输入“0”,回车,并根据本机的IP地址配置tftp服务器IP地址:(设本机

ip:218.192.169.4)

set myipaddr 218.192.169.5

set destipaddr 218.192.169.4

16、用提供的直连网线连接开发板和pc机,并将要烧写的内核和文件系统复

制到C:\Cisco TFTP Server\tftp目录;

17、超级终端中输入:tftp zImage_e24qt kernel回车,此时开始传输内核镜像

文件,完成后输入:flash kernel回车开始烧写内核;

18、在在超级终端中输入:tftp rootfs270qt_24.img root 回车,此时开始传

输文件系统镜像文件,完成后输入:flash root回车开始烧写文件系统;

此时嵌入式linux 的bootloader、内核、文件系统均已烧写到开发板,重

启开发板电源,开发板的linux系统就可以正常启动了,在系统登陆用户

中输入“root”,系统就登陆到linux 系统了。

19、编写简单应用程序,例如“helloworld.c” ,上传到服务器进行编译

(arm-linux-gcc helloworld.c -o hello),并将可执行文件下载到本机。

20、在超级终端中输入:cd /tmp,然后点击菜单发送->传送文件,选择hello

文件将其下载到cd /tmp目录。

21、运行hello:./hello; 如果没有权限则修改权限:chmod +x hello

五、 程序说明

FlashLoader源代码分析:

PXA270的Flashloader定义在start.S中:

初始化GPIO(定义在fixgpio.S )

bl define_gpio

设置CPU的速度和时钟频率

bl clock_enable

- 10 -

嵌入式系统实验报告

存储控制单元初始化

bl setup_memory

复制bootloader到SDRAM

ldr r0, =0x00//原地址

ldr r1, =__boot_start//运行地址

ldr r2, =__boot_end

1: ldmia r0!, {r3-r10}

stmia r1!, {r3-r10}

cmp r1, r2

blt 1b

设置网卡基地址

ldr r0, =eth_base

ldr r1, [r0]

设置栈指针

ldr sp, =stack_point-4

跳转到main函数

ldr pc, =main

执行完FlashLoader,CPU控制权移交给BootLoader,开始执行BootLoader的main()函数。

PXA270开发板BootLoader的主要操作为:

? uart_init();//串口初始化

? time_init();//初始化定时器

? config_init();//内存映射配置初始化

? iflash_init();//flash存储器初始化

? eth_init();//网络初始化

– autoboot_mode();//进入自动启动模式

– 进入bootloader 下载模式

? 进入autoboot模式后,先将内核和Ramdisk分别拷贝到0xa000,8000和0xa100,0000 ? 跳转到KERNEL:(do_boot函数)

通过定义void (*kernel)(int zero, int arch);及Kernel = 内核的SDRAM地址,实现跳转。 create_tags();//设置内核的启动参数

kernel(0, 200);//直接跳转到内核开始地址去

六、 程序源代码、注释

(1)阶段1源程序分析

Start.s 源程序如下:

#include <config.h>

- 11 -

#include <hardware.h>

#define eth_base 0x0c00030e

.section .text.start

.global _start

_start :

b start

.section .text

start :

bl define_gpio //初始化GPIO

bl clock_enable //设置CPU的速度和时钟频率

bl setup_memory //@ setup static and dynamic memory存储控制单//元初始化

@ldr r0, =0x0a000000

@ldr r1, =0xfff

@str r1,[r0]

@ copy bootloader to dynamic memory area //复制bootloader到SDRAM

ldr r0, =0x00 //原地址

ldr r1, =__boot_start //运行地址

ldr r2, =__boot_end

1: ldmia r0!, {r3-r10}

stmia r1!, {r3-r10}

cmp r1, r2

blt 1b

@ clear bss area //设置网卡基地址

mov r3, #0x00

mov r4, #0x00

mov r5, #0x00

mov r6, #0x00

ldr r0, =bss_start

ldr r1, =bss_end

1: stmia r0!, {r3-r6}

cmp r0, r1

blt 1b

ldr r0, =eth_base

ldr r1, [r0]

@ set stack point //设置栈指针

ldr sp, =stack_point-4

@ jump to c code // 跳转到main函数

- 12 -

ldr pc, =main

clock_enable :

#if defined(CONFIG_PXA25x)

ldr r0, =0x0001FFFF

#elif defined(CONFIG_PXA27x)

ldr r0, =0x01FFFFFF

#endif

ldr r1, =CKEN

str r0, [r1]

mov pc, lr

(2)阶段2程序分析

执行完FlashLoader,CPU控制权移交给BootLoader,开始执行BootLoader的main()函数。

uart_init();//串口初始化

time_init();//初始化定时器

config_init();//内存映射配置初始化

iflash_init();//flash存储器初始化

eth_init();//网络初始化

autoboot_mode();//进入自动启动模式

进入bootloader 下载模式

实验中下载到开发板运行的一个“Hello,world!”程序源代码:

#include <stdio.h>

int main()

{

printf(“Hello world!\n”,0);

return 0;

}

七、 总结(心得体会)

这次实验过程中没有接触太多的程序,可能因为程序过于复杂的原因。并且实验本身操作起来比较的复杂,需要用到很多的软件,总之比较麻烦不容易清楚的认识整个实验。

实验中,主要学习了熟悉嵌入式linux环境下的开发工具,包包括make、gcc、超级终端等,还有内核和文件系统的编译和下载。Bootloader这个底层程序完成了上层文件安装的驱动引导作用,让我基本的了解BootLoader的基本概念和框架结构。了解了bootloader的实现原理、结构和运行过程,丰富了自己的见识。

最后,在将hello文件下载到cd /tmp目录之前,要先在服务器上对hello.c文件进行编译,再运用超级终端这个工具把hello.o下载到开发板上去。

- 13 -

相关推荐