免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 6224 | 回复: 14
打印 上一主题 下一主题

[转]基于ARM的嵌入式BootLoader设计与启动过程 [复制链接]

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-07-08 16:20 |只看该作者 |倒序浏览
作者:单承刚 戴学丰 刘树东 崔登志  来源于: 微计算机信息  发布时间:2007-5-26 11:34:00
摘要:本文总结了基于S3C44B0X芯片的Bootloader代码设计的一般流程,具体说明了Bootloader完成的主要任务,实现方法和地址重映射的概念;给出了S3C44B0X在基于uCLinux操作系统的嵌入式应用中,Bootloader的引导过程及关键技术和代码,有着很好的借鉴价值。
关键词: 嵌入式系统  Bootloader代码  地址重映射  S3C44B0X  uCLinux  

1 引言

近年来,ARM在嵌入式设备中的应用越来越广泛。S3C44B0X就是Samsung公司生产的基于ARM7架构的微处理器。uCLinux是专门为无MMU处理器设计的嵌入式操作系统,支持ARM、Motorola等处理器。采用ARM+uCLinux作为嵌入式开发平台,吸取了两者优点,是一种比较流行的开发方式。在ARM程序设计中,启动代码是至关重要的,它不仅初始化硬件系统本身,而且最终引导进入操作系统,所以启动代码的设计直接影响整个开发的正常进行。本文结合S3C44B0X的特点详细给出了Bootloader设计和启动过程。

2 BootLoader代码

    Bootloader代码是芯片复位后进入操作系统之前执行的一段代码,主要是为操作系统提供基本的运行环境,由它最终将操作系统启动起来并将控制权交给操作系统内核。

Bootloader代码由汇编代码和C代码两部分组成。由于资源有限,Bootloader、uCLinux内核映象、文件系统(Ramdisk压缩映象)通常都是固化在Flash中的。在S3C44B0X架构的嵌入式系统中,为了保证上电或复位时正常运行, Bootloader必须存放在Flash(0x0-0x400000,

4M)中的0x0处,则在硬件设计中把Flash接在CPU的nGCS0处[1]。上电后 Bootloader首先完成存储器、堆栈、寄存器、全局变量和基本硬件模块的初始化,这由汇编代码完成,然后将操作系统内核与文件系统调入SDRAM (0x0c000000-0x0c800000,8M)中,并将PC指针指向操作系统内核的入口处,为操作系统的运行作好准备,这由C代码完成。

3 BootLoader代码设计流程

    ARM芯片多数为复杂的片上系统(SOC),系统里许多硬件模块都可由软件来设置。系统的初始化直接联系到处理器内核和硬件控制器,进行编程一般用汇编语言。根据具体的目标系统,Bootloader的设计流程包括:⑴设置异常向量表 ⑵初始化存储器系统 ⑶堆栈初始化 ⑷C例程全局变量初始化 ⑸呼叫主程序

3.1 设置异常中断向量表

ARM处理器的中断向量表从地址0x0处开始存放,连续有8×4字节的空间。在ARM存储空间里每个字32位,占4个字节。可以通过图1来描述中断向量表的地址分配。

每当有中断或者异常发生时,ARM处理器便强制把PC指针指向向量表中对应中断类型的地址值。为了加快中断响应,我们在Flash的0x0地址存放能跳转到0x0c000008地址处中断向量的跳转指令,即在RAM中建立一个二级中断向量表,起始地址为0x0c000008,除复位外,其它异常入口地址由Flash跳转得到,如表1所示:

       图 1 中断向量表                 表 1 异常向量表跳转关系

3.2 初始化存储器系统

存储器系统的初始化是指对Flash、RAM存储器的地址范围,数据总线宽度及DRAM的刷新等进行软件设置。设置对象是存储器控制寄存器,芯片不同具体的设置不同。本文仅以开发中用到S3C44B0X为例,说明这部分程序设计的过程。

memsetup:  ldr       r0, =MEMORY_CONFIG

               ldmia       r0, {r1-r13}

               ldr          r0, =0x01c80000

               stmia       r0, {r1-r13}

其中MEMORY_CONFIG地址处定义了配置rBWSCON, rBANKCONn(n=0-7),rREFRESH,

rBANKSIZE,rMRSRB6,rMRSRB7寄存器的值(依次为0x11000100,0x700,0x700,0x7ffc,0x7568

,0x700,0x700,0x18008,0x18000,0xac03e1,0x16,0x020,0x020)。

图 2 地址映射关系

地址重映射通常把位于Flash的0x0处异常向量表映射到更快、更宽(32bit/16bit)的RAM中,并把0地址重新指向到RAM中去.图2描述了Remap前后的地址映射关系,复位时ROM定位到0x0,实际的跳转地址为ROM的Reset Handler处,Remap后0x0的ROM替换为RAM,并把中断向量表拷贝到0x0。Remap的实现和ARM处理器硬件特性相关。在 S3C44B0X系统中,既没有MMU也不支持Remap,为了实现快的启动和异常处理速度,在Bootloader程序设计中,我们把RAM中的 0x0c000008地址当成0x0地址处理,即在这里开始存放程序中断向量,而在Flash的0x0地址存放能跳转到0x0c000008地址处中断向量的跳转指令。支持Remap的处理器有的通过寄存器的相应Bit位置1来完成,有的通过软件设计改写Memory起始地址的Bank寄存器完成[2]。

[ 本帖最后由 dreamice 于 2008-7-8 16:25 编辑 ]

figure1.gif (7.64 KB, 下载次数: 58)

figure1.gif

figure2.gif (4.91 KB, 下载次数: 67)

figure2.gif

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
2 [报告]
发表于 2008-07-08 16:21 |只看该作者

继续:

3.3 初始化堆栈

ARM处理器有7种工作模式,每种模式都有独立的堆栈指针寄存器(SP),并定义相应地址。改变状态寄存器(CPSR)的状态位,可使处理器切换到不同模式,然后给SP赋值,就实现了堆栈的初始化。需注意的是:不要切换到用户模式进行本模式的堆栈设置,因为进入该模式后就不能修改CPSR回到别的模式了,会影响程序的顺利执行[3]。初始化堆栈的代码如下所示:(以2个不同的SP寄存器SP_IRQ、SP_FIQ为例)

InitStacks:  mrs  r0,cpsr                 ;CPSR=》R0

             bic  r0,r0,#MODEMASK|NOINT   ;屏蔽模式位和中断

orr  r1,r0,#IRQMODE|NOINT    ;MODEMASK =0x1f, NOINT = 0x80

            msr  cpsr_cxsf,r1            ;转到IRQ模式

            ldr  sp,=IRQStack            ;设置SP_irq

             orr  r1,r0,#FIQMODE            

            msr  cpsr_cxsf,r1             ;转到FIQ模式

            ldr  sp,=FIQStack  

3.4 C例程全局变量初始化

全局变量的初始化,就是完成从ROM到RAM的数据传输和内容清零。可执行程序的映像结构由RO段、RW段和ZI段三部分组成,分别为只读数据段、可写数据段和堆栈段。其中RO段在Flash和RAM里都可运行;而RW和ZI段是必须转移到RAM中去的。尽管RAM的运行速度比Flash快的多,但由于RO段比较小,拷贝到RAM也需要时间,还要程序跳转,一比较两者的启动时间差不多,最终我们选择让RO段在Flash中运行。

开发工具中的链接器(Linker)提供了一定的机制来帮助我们完成这部分工作,其中|Image$$ZI$$ Base|,|Image$$ZI$$Limit|,|Image$$RW$$Base|,|Image$$RO$$Limit|是由链接器定义输出的。主要是输出段的起始和终止定位信息,具体程序实现如下:

startram :  LDR    a1,=|Image$$ZI$$Base|    ;ZI段在RAM里面的起始地址

            MOV   a3,#0                    ;寄存器清0               

            LDR   a2,=|Image$$ZI$$Limit|   ;ZI段在RAM里面的结束地址

            CMP   a1,a2                                                

            BEQ   move_data                                            

clear_loop : STR   a3,[a1],#4               ;清一个字为0, a1 += 4   

            CMP   a1,a2                                                   

            BNE   clear_loop                                                                                                                                                                                      

move_data   LDR    a1,=|Image$$RW$$Base|    ;RW段在RAM中的起始地址   

           LDR    a2,=|Image$$RO$$Limit|   ;RW段在ROM中的起始地址   

            LDR    a3,=|Image$$ZI$$Base|    ;RW段在RAM中的结束地址                                                                              

           CMP    a1,a3                                                

           BEQ    goto_main                                            

move_loop : LDR    a4,[a2],#4                                          

           STR    a4,[a1],#4               ;拷贝一个字,a1 += 4, a2 += 4

           CMP    a1,a3                                 

           BNE    move_loop                                                                                         

goto_main :  BL   Main

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
3 [报告]
发表于 2008-07-08 16:22 |只看该作者

接上

3.5 呼叫主程序

当系统初始化完成后,就要转入主程序,可由跳转指令来完成。这部分代码为C程序,主要负责uCLinux内核拷贝、 Ramdisk文件系统加载、设置启动参数和串口通信等功能.程序最终完成内核压缩文件和Ramdisk到RAM的拷贝,后跳转到RAM中Linux Kernel的首地址,并交出控制权,到此Bootloader的任务完成。其中uCLinux在RAM中的空间分配为0x0c000000

-0x0c008000(uCLinux内核调度用),0x0c008000-0x0c300000(uCLinux Kernel),0x0c300000

-0x0c800000(uCLinux Ramdisk)。以下程序为uCLinux内核和Ramdisk文件系统拷贝过程,FLASH_UC_KERNEL,FLASH_UC_RAMDISK分别为内核和文件系统在Flash的起始地址,RAM_UC

_ZIMAGE,RAM_UC_RAMDISK分别为RAM中uCLinux内核和文件系统的起始地址,FLASH_LEN_UC_

KERNEL,FLASH_LEN_UC_RAMDISK分别为两者大小,拷贝完后,PC指针指向RAM_UC_ZIMAGE。程序主要部分如下:

/* Copy  uClinux Kernel */

    pSource = (unsigned int *)FLASH_UC_KERNEL;

    pDest = (unsigned int *)RAM_UC_ZIMAGE;

    for (loopcnt = 0;loopcnt < (FLASH_LEN_UC_KERNEL>>2);loopcnt++)

    {   *pDest++ = *pSource ++;  }

/* Copy ramdisk */

    pSource = (unsigned int *)FLASH_UC_RAMDISK;

    pDest = (unsigned int *)RAM_UC_RAMDISK;

    for (loopcnt = 0;loopcnt< (FLASH_LEN_UC_RAMDISK>>2); loopcnt++)

    {   *pDest++ = *pSource ++;  }

/* Start Kernel */

    fp = (UClinux_Entry)RAM_UC_ZIMAGE;

    (*fp)(0);

4  uCLinux操作系统启动过程

本系统采用RAM中启动方式,将Flash中的内核先拷贝到RAM的某一段地址区间,再从该区间的首地址运行 uCLinux内核。当Bootloader完成系统初始化和拷贝完内核和Ramdisk以后,交出控制权,系统开始从RAM中执行uCLinux内核的引导程序Head.s,并将控制权交给它。在uCLinux中,Head.s在uCLinux-dist/linux- 2.4.x/arch/armnommu/boot/compressed/head.s

里。Head.s非常关键,它完成了加载内核的大部分工作,主要是调用Misc.c中的解压内核函数 (decompress_kernel)来完成,另外内核的加载还必须知道系统必要的硬件信息,该硬件信息在hardware.h中并被Head.s所引用。最后跳转到调用内核函数(call_kernel),将控制权交给解压后的uCLinux系统[4]。系统启动后将加载Ramdisk文件系统,进入用户shell程序。
5  结束语

   本系统采用Bootloader完成操作系统内核和文件系统拷贝到RAM的设计方法,提高了系统的实时性。目前,启动代码已经正常引导uCLinux操作系统,并实现了对轮式移动机器人驱动系统的控制,该嵌入式系统运行稳定,完全实现了设计目的。由于所选处理器的关系,本文的Bootloader是不支持Remap芯片的,但启动装载程序的原理都一样,只要稍作修改就可用于支持Remap和MMU的芯片,而且对于其他操作系统同样适用。

参考文献

1 严国清,熊建设.S3C44B0X中BootLoader的实现.[J].数据采集与嵌入式系统.2004,(6):40-43

2 姜京梅,郭树旭.ARM7启动代码设计方法与流程.[J].微计算机信息,2004,(20):78-79

3 费浙平.基于ARM的嵌入式系统开发—系统的初始化过程.[J].单片机与嵌入式系统应用,2003.9,80-83

4 黄克彬,叶 梧,冯穗力.基于ARM+uCLinux嵌入式系统启动引导的实现.[J].电子技术应用. http://

www.cvtech.com.cn/tech_detail.asp?newsid=223

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
4 [报告]
发表于 2008-07-08 16:23 |只看该作者
不知道为什么,贴不上图。希望驱动版面能够活跃起来!

论坛徽章:
0
5 [报告]
发表于 2008-07-08 16:49 |只看该作者
对嵌入式不是很懂

请问嵌入式bootloader不需要保护模式吗

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
6 [报告]
发表于 2008-07-08 17:14 |只看该作者
bootloader是系统启动前加载的一段代码,在X86上相当于bios执行的那段代码,所以应该是运行于实模式的。一般的嵌入式系统,bootloader也分为两个部分,实际上都是在运行操作系统之前执行的代码。不知道我说清楚没有。

论坛徽章:
0
7 [报告]
发表于 2008-07-08 17:40 |只看该作者
那么,uclinux会不会进入保护模式?
uclinux和linux出了mmu的区别,其他的多吗?

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
8 [报告]
发表于 2008-07-08 17:52 |只看该作者
对uCLinux 来说,其设计针对没有MMU的处理器,不能使用处理器的虚拟内存管理技术。uCLinux仍然采用存储器的分页管理,系统在启动时把实际存储器进行分页。uCLinux系统对于内存的访问是直接的,所有程序中访问的地址都是实际的物理地址。操作系统对内存空间没有保护,各个进程实际上共享一个运行空间。一个进程在执行前,系统必须为进程分配足够的连续地址空间,然后全部载入主存储器的连续空间中。
uClinux中没有fork()和brk()系统调用,调用fork()将复制出进程来创建一个子进程。在Linux下,fork()是使用copy-on-write页面来实现的。由于没有MMU, uclinux不能完整、可靠地复制一个进程,也没有对copy-on-write的存取。uClinux实现了vfork(),当父进程调用vfork()来创建子进程时,两个进程共享它们的全部内存空间,包括堆栈。子进程要么代替父进程执行(此时父进程已经sleep)直到子进程调用exit()退出,要么调用exec()执行一个新的进程,这个时候将产生可执行文件的加载。

论坛徽章:
3
金牛座
日期:2014-06-14 22:04:062015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:45
9 [报告]
发表于 2008-07-08 17:55 |只看该作者
uClinux是针对控制领域的嵌入式linux操作系统,它从Linux 2.0/2.4内核派生而来,沿袭了主流Linux的绝大部分特性。适合不具备内存管理单元(MMU)的微处理器/微控制器。
其他的差异,不是特别了解。我想,uClinux的中进程的调度策略,文件系统等等,要比linux标准版本滞后一些(个人猜测)。

论坛徽章:
0
10 [报告]
发表于 2008-07-08 18:01 |只看该作者
谢谢你的回复。
这么说,uclinux的父子进程不能同时运行了?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP