BootLoader—vivi注释
- 格式:doc
- 大小:149.00 KB
- 文档页数:20
GET ..\inc\option.s //相当于c语言中的#include "option.s"GET ..\inc\memcfg.s;Interrupt Control //中断控制器的特殊功能寄存器对应的地址INTPND EQU 0x01e00004INTMOD EQU 0x01e00008INTMSK EQU 0x01e0000cI_ISPR EQU 0x01e00020I_CMST EQU 0x01e0001c;Watchdog timer //看门狗定时器WTCON EQU 0x01d30000;Clock Controller //时钟控制器PLLCON EQU 0x01d80000CLKCON EQU 0x01d80004LOCKTIME EQU 0x01d8000c;Memory Controller //内存控制器REFRESH EQU 0x01c80024 ;Dram/sdram刷新控制寄存器;Pre-defined constants //预定义的常量;下面是对arm处理器模式寄存器对应值的常数定义,arm处理器中有一个CPSR程序状态寄存器它的后五位决定目前的处理器模式;0b10000用户模式;0b10001FIQ模式等USERMODE EQU 0x10FIQMODE EQU 0x11IRQMODE EQU 0x12SVCMODE EQU 0x13ABORTMODE EQU 0x17UNDEFMODE EQU 0x1bMODEMASK EQU 0x1fNOINT EQU 0xc0;arm处理器有两种工作状态 1.arm:32位这种工作状态下执行字对准的arm指令 2.Thumb:16位这种工作状态执行半字对准的Thumb指令;因为处理器分为16位 32位两种工作状态程序的编译器也是分16位和32两种编译方式所以下面的程序用于根据处理器工作状态确定编译器编译方式;code16伪指令指示汇编编译器后面的指令为16位的thumb指令;code32伪指令指示汇编编译器后面的指令为32位的arm指令;这段是为了统一目前的处理器工作状态和软件编译方式(16位编译环境使用tasm.exe编译);check if tasm.exe is used.GBLL THUMBCODE ;设置一个全局逻辑变量[ {CONFIG} = 16THUMBCODE SETL {TRUE}CODE32|THUMBCODE SETL {FALSE}];if config==16 这里表示你的目前处于领先地16位编译方式,设置THUMBCODE 为 true,否则就转入32位编译模式,设置THUMBCODE 为 false[ THUMBCODECODE32 ;for start-up code for Thumb mode]注意下面这段程序是个宏定义;这段程序用于把中断服务程序的首地址装载到pc中,有人称之为“加载程序”。
Bootloader 的学习/user1/5809/archives/2009/60942.htmlBootload: 在操作系统内核运行前运行的一段程序,相当于PC机上的BIOS, 用于初始化硬件设备,和建立内存空间的映射,从而将系统的软硬件环境带到一个合适的状态。
复位后,从地址0x 0000 0000 处取它第一条指令。
典型的嵌入式系统Bootload 有 Blob (boot load object) 和 U-boot (universal boot loader)其中U-boot 支持ARM, MIPS, X86, Nios,可启动VxWorks,QNX,Linux在flash里,首先存放bootload, 其次启动参数,是内核和根文件系统bootloader分两个部分,stage1和stage2:stage1: 汇编部分执行简单的硬件初始化.1. 硬件设备初始化2. 为加载bootloader的stage2准备RAM空间3. 设置好堆栈4. 跳转到stage2的C入口stage2: C语言部分负责复制数据,设置启动参数和串口通信等1. 初始化本阶段使用到的硬件设备2. 检测系统内存映像3. 将kernel映像和根文件映像从flash读到RAM中4. 为内核设置启动参数5. 调用内核------------------------------------Mini2440 的Bootloader 结构分析1.Import some head files.GET option.inc ;GET = includeGET memcfg.incGET 2440addr.incoption.inc 顾名思义是可以改变的配置选项,其中包括一些起始地址,初始状态,总线宽度,时钟频率。
如果以后对系统的设置有变化,直接修改”option.inc”中的配置即可,无需大量改动boot loader.对mini2440中option.inc分析如下:_STACK_BASEADDRESS EQU 0x33ff8000; STACK 的起始地址_MMUTT_STARTADDRESS EQU 0x33ff8000; 定义MMU表基地址_ISR_STARTADDRESS EQU 0x33ffff00;补充MMU知识:a.什么是MMUMMU –Memory Management Unit 缩写,用来管理virtual memory和physical memory的控制线路。
S3C2410完全开发流程作者:thisway.diy@ 一.简介 (2)二.建立开发环境 (3)(1)编译器arm-linux-gcc-3.4.1 (3)(2)Jflash-s3c2410:S3C2410芯片的JTAG工具 (3)(3)安装gdb调试工具 (3)三.S3C2410基础实验 (4)(1)实验一:LED_ON (4)(2)实验二:LED_ON_C (5)(3)实验三:I/O PORTS (7)(4)实验四:arm-linux-ld (9)(5)实验五:MEMORY CONTROLLER (10)(6)实验六:NAND FLASH CONTROLLER (13)(7)实验七:UART (15)(8)实验八:printf、scanf (18)(9)实验九:INTERRUPT CONTROLLER (19)(10)实验十:TIMER (23)(11)实验十一:MMU (25)(12)实验十二:CLOCK (33)四.Bootloader vivi (36)(1)阶段1:arch/s3c2410/head.S (36)(2)阶段2:init/main.c (37)1、Step 1:reset_handler() (38)2、Step 2:board_init() (39)3、Step 3:建立页表和启动MMU (39)4、Step 4:heap_init() (42)5、Step 5:mtd_dev_init() (44)6、Step 6:init_priv_data() (49)7、Step 7:misc()和init_builtin_cmds() (50)8、Step 8:boot_or_vivi() (51)一.简介本书面向由传统51单片机转向ARM嵌入式开发的硬件工程师、由硬件转嵌入式软件开发的工程师、没有嵌入式开发经验的软件工程师。
分9个部分:1、开发环境建立2、S3C2410功能部件介绍与实验(含实验代码)3、bootloader vivi详细注释4、linux移植5、linux驱动6、yaffs文件系统详解7、调试工具8、GUI开发简介9、UC/OS移植通过学习第二部分,即可了解基于ARM CPU的嵌入式开发所需要的外围器件及其接口。
凌FL2440超详细U-BOOT作业(UBoot介绍+H-jtag使用+Uboot使用)Bootloader是高端嵌入式系统开发不可或缺的部分。
它是在操作系统内核启动之前运行的一段小程序。
通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。
现在主流的bootloader有U-BOOT、vivi、Eboot等。
本次作业先做Uboot的烧写吧。
希望通过这个帖子,能让更多的初学者朋友了解一些UBoot的知识,也希望高手朋友对我的不足予以斧正。
首先说一下什么是Uboot:U-Boot,全称Universal Boot Loader,是遵循GPL条款的开放源码项目。
从FAD SROM、8xxROM、PPCBOOT逐步发展演化而来。
其源码目录、编译形式与Linux内核很相似,事实上,不少U-Boot源码就是相应的Linux内核源程序的简化,尤其是一些设备的驱动程序,这从U-Boot源码的注释中能体现这一点。
但是U-Boot不仅仅支持嵌入式Linu x系统的引导,当前,它还支持NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS嵌入式操作系统。
其目前要支持的目标操作系统是OpenBSD, NetBSD, FreeBSD,4.4BSD, Linux, SVR4, Esix, Solaris, Irix, SCO, Dell, NCR, VxWorks, LynxOS, pSOS, QNX, RTEMS, ARTOS。
这是U-Boot中Universal的一层含义,另外一层含义则是U-Boot除了支持PowerPC系列的处理器外,还能支持MIPS、x86、ARM、NIOS、XScale等诸多常用系列的处理器。
这两个特点正是U-Boot项目的开发目标,即支持尽可能多的嵌入式处理器和嵌入式操作系统。
SBC-2410X使用手册Version0.9广州友善之臂科技有限公司二零零四年十月(请拿到板子后请务必先阅读说明书,谨防随意破坏系统启动程序bootloader(vivi)修正2. 第145页:添加ld.so.conf内容(2004-11-1)1. 页眉的“祝您一臂之力”改为“助您一臂之力”(2004-11-1)前言SBC-2410X是由广州友善之臂科技有限公司设计生产的一款基于ARM9的嵌入式电脑平台,它基于三星公司的ARM处理器S3C2410X,采用6层板设计。
S3C2410X使用ARM920T核,内部带有全性能的MMU(内存处理单元),它适用于设计移动手持设备类产品,具有高性能、低功耗、接口丰富和体积小等优良特性。
SBC-2410X正是基于此芯片本身的各种特点而设计的。
SBC-2410X的设计遵循了S3C2410X嵌入式芯片的特点,其设计理念参考了当前市场上众多的开发板及嵌入式单板机的优点,并融入最新掌上电脑/手持设备的特点,因此是一款单板机和开发板两用的嵌入式电脑平台。
侧面复位按键的设计位置恰到好处,不会阻挡用户罩在板上的面板;板载采用高质量进口按键,手感特别舒适,经久耐用,而且用户可以随心所欲设计自己的键盘外观,而不需要修改驱动程序;精致的1220型板载备份电池更可以保持时间随时随地准确无误;20针的标准JTAG接口让您可以方便的连接各种仿真器;SD卡、各种优盘移动硬盘、音频输入输出、串口、RJ-45网线等即插即用;定位孔的设计参考了市面上大量的液晶屏模块尺寸,用户可以使用现成的或者设计自己的液晶模块,扣在板上,完全就是一个标准的PDA。
你可以使用套件提供的各种开发工具按照自己的意图或需要设计各种各样的应用程序,而我们的网站也提供了很多精彩的应用制作实例供您参考,这些实例均有映象文件可以下载演示;整个板子的尺寸只有120mmx90mm,这仅相当于一个普通PDA的大小。
基于以往我们的产品反馈意见,SBC-2410X进行了严格的电磁,温度,高压脉冲,老化,灰尘等测试,性能稳定,可代替部分工控单板机。
简单地说,Boot Loader 就是在操作系统内核运行之前运行的一段小程序。
通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。
通常,Boot Loader 是严重地依赖于硬件而实现的,特别是在嵌入式世界。
因此,在嵌入式世界里建立一个通用的Boot Loader 几乎是不可能的。
尽管如此,我们仍然可以对Boot Loader 归纳出一些通用的概念来,以指导用户特定的Boot Loader 设计与实现。
1. Boot Loader 所支持的CPU 和嵌入式板每种不同的CPU 体系结构都有不同的Boot Loader。
有些Boot Loader 也支持多种体系结构的CPU,比如U-Boot 就同时支持ARM 体系结构和MIPS 体系结构。
除了依赖于CPU的体系结构外,Boot Loader 实际上也依赖于具体的嵌入式板级设备的配置。
这也就是说,对于两块不同的嵌入式板而言,即使它们是基于同一种CPU 而构建的,要想让运行在一块板子上的Boot Loader 程序也能运行在另一块板子上,通常也都需要修改Boot Loader 的源程序。
2. Boot Loader 的安装媒介(Installation Medium)系统加电或复位后,所有的CPU 通常都从某个由CPU 制造商预先安排的地址上取指令。
比如,基于ARM7TDMI core 的CPU 在复位时通常都从地址0x00000000 取它的第一条指令。
而基于CPU 构建的嵌入式系统通常都有某种类型的固态存储设备(比如:ROM、EEPROM 或FLASH 等)被映射到这个预先安排的地址上。
因此在系统加电后,CPU 将首先执行Boot Loader 程序。
3. 用来控制Boot Loader 的设备或机制主机和目标机之间一般通过串口建立连接,Boot Loader 软件在执行时通常会通过串口来进行I/O,比如:输出打印信息到串口,从串口读取用户控制字符等。
s3c2410 bootloader vivi完全注释四.Bootloader vivi 为了将linux移植到ARM上,碰到的第一个程序就是bootloader,我选用韩国mizi公司的vivi。
您可以在以下地址下载:/developer/s3c2410x/download/vivi.html如果您对bootloader没有什么概念,在学习VIVI的代码之前,建议您阅读一篇文章《嵌入式系统 Boot Loader 技术内幕》(詹荣开著)。
链接地址如下:/developerworks/cn/linux/l-btloader/当您阅读了上述文章后,我再企图在理论上罗嗦什么就不合适了(这篇文章实在太好了)。
vivi也可以分为2个阶段,阶段1的代码在 arch/s3c2410/head.S 中,阶段2的代码从init/main.c的main函数开始。
您可以跳到实验部分先感受一下vivi。
(1)阶段1:arch/s3c2410/head.S 沿着代码执行的顺序,head.S完成如下几件事情:1、关WATCH DOG:上电后,WATCH DOG默认是开着的2、禁止所有中断:vivi中没用到中断(不过这段代码实在多余,上电后中断默认是关闭的)3、初始化系统时钟:启动MPLL,FCLK=200MHz,HCLK=100MHz,PCLK=50MHz,“CPU bus mode”改为“Asynchronous bus mode”。
请参考实验十一:CLOCK4、初始化内存控制寄存器:还记得那13个寄存器吗?请复习实验五:MEMORY CONTROLLER5、检查是否从掉电模式唤醒,若是,则调用WakeupStart函数进行处理——这是一段没用上的代码,vivi不可能进入掉电模式6、点亮所有LED7、初始化UART0:a.设置GPIO,选择UART0使用的引脚b.初始化UART0,设置工作方式(不使用FIFO)、波特率115200 8N1、无流控等,请参考实验七:UART8、将vivi所有代码(包括阶段1和阶段2)从nand flash复制到SDRAM中:a.设置nand flash控制寄存器b.设置堆栈指针——调用C函数时必须先设置堆栈c.设置即将调用的函数nand_read_ll的参数:r0=目的地址(SDRAM的地址),r1=源地址(nand flash的地址),r2=复制的长度(以字节为单位)d.调用nand_read_ll进行复制e.进行一些检查工作:上电后nand flash最开始的4K代码被自动复制到一个称为“Steppingstone”的内部RAM中(地址为0x00000000-0x00001000);在执行nand_read_ll之后,这4K代码同样被复制到SDRAM中(地址为0x33f00000-0x33f01000)。
比较这两处的4K代码,如果不同则表示出错9、跳到bootloader的阶段2运行——就是调用init/main.c中的main函数:a.重新设置堆栈b.设置main函数的参数c.调用main函数如果您做了第二章的各个实验,理解head.S就不会有任何困难——上面说到的几个步骤,都有相应的实验。
head.S有900多行,把它搬到这篇文章上来就太不厚道了——白白浪费页数而已。
在vivi/arch/s3c2410/head.S上,我做了比较详细的注释(其中的乱码可能是韩文,不去管它)。
(2)阶段2:init/main.c 本阶段从init/main.c中的main函数开始执行,它可以分为8个步骤。
我先把main函数的 肼蘖腥缦?去掉了乱码——可能是韩文,现在没留下多少有用的注释了),然后逐个分析:1 int main(int argc, char *argv[])2 {3 int ret;4 /*Step 1*/5 putstr("\r\n");6 putstr(vivi_banner);7 reset_handler();8 /*Step 2*/9 ret = board_init();10 if (ret) {11 putstr("Failed a board_init() procedure\r\n");12 error();13 }14 /*Step 3*/15 mem_map_init();16 mmu_init();17 putstr("Succeed memory mapping.\r\n");18 /* Now, vivi is running on the ram. MMU is enabled.*/19 /*Step 4*/20 /* initialize the heap area */21 ret = heap_init();22 if (ret) {23 putstr("Failed initailizing heap region\r\n");24 error();25 }26 /*Step 5*/27 ret = mtd_dev_init();28 /*Step 6*/29 init_priv_data();30 /*Step 7*/31 misc();32 init_builtin_cmds();33 /*Step 8*/34 boot_or_vivi();35 return 0;36 }1、Step 1:reset_handler() reset_handler用于将内存清零,代码在lib/reset_handle.c中。
[main(int argc, char *argv[]) > reset_handler()]1 void2 reset_handler(void)3 {4 int pressed;5 pressed = is_pressed_pw_btn(); /*判断是硬件复位还是软件复位*/6 if (pressed == PWBT_PRESS_LEVEL) {7 DPRINTK("HARD RESET\r\n");8 hard_reset_handle(); /*调用clear_mem对SDRAM清0*/9 } else {10 DPRINTK("SOFT RESET\r\n");11 soft_reset_handle(); /*此函数为空*/12 }13 }在上电后,reset_handler调用第8行的hard_reset_handle(),此函数在lib/reset_handle.c中:[main(int argc, char *argv[]) > reset_handler() > hard_reset_handle()]1 static void2 hard_reset_handle(void)3 {4 #if 05 clear_mem((unsigned long)(DRAM_BASE + VIVI_RAM_ABS_POS), \6 (unsigned long)(DRAM_SIZE - VIVI_RAM_ABS_POS));7 #endif8 /*lib/memory.c,将起始地址为USER_RAM_BASE,长度为USER_RAM_SIZE 的内存清0*/9 clear_mem((unsigned long)USER_RAM_BASE, (unsignedlong)USER_RAM_SIZE);10 }2、Step 2:board_init() board_init调用2个函数用于初始化定时器和设置各GPIO引脚功能,代码在arch/s3c2410/smdk.c中:[main(int argc, char *argv[]) > board_init()]1 int board_init(void)2 {3 init_time(); /*arch/s3c2410/proc.c*/4 set_gpios(); /*arch/s3c2410/smdk.c*/5 return 0;6 }init_time()只是简单的令寄存器TCFG0 = 0xf00,vivi未使用定时器,这个函数可以忽略。
set_gpios()用于选择GPA-GPH端口各引脚的功能及是否使用各引脚的内部上拉电阻,并设置外部中断源寄存器EXTINT0-2(vivi中未使用外部中断)。
3、Step 3:建立页表和启动MMU mem_map_init函数用于建立页表,vivi使用段式页表,只需要一级页表。
它调用3个函数,代码在arch/s3c2410/mmu.c 中:[main(int argc, char *argv[]) > mem_map_init(void)]1 void mem_map_init(void)2 {3 #ifdef CONFIG_S3C2410_NAND_BOOT /* CONFIG_S3C2410_NAND_BOOT=y */4 mem_map_nand_boot(); /* 最终调用mem_mepping_linear,建立页表 */5 #else6 mem_map_nor();7 #endif8 cache_clean_invalidate(); /* 清空cache,使无效cache */9 tlb_invalidate(); /* 使无效快表TLB */10 }第9、10行的两个函数可以不用管它,他们做的事情在下面的mmu_init函数里又重复了一遍。
对于本开发板,在.config中定义了CONFIG_S3C2410_NAND_BOOT。
mem_map_nand_boot()函数调用mem_mapping_linear()函数来最终完成建立页表的工作。
页表存放在SDRAM物理地址0x33dfc000开始处,共16K:一个页表项4字节,共有4096个页表项;每个页表项对应 1M地址空间,共4G。
mem_map_init先将4G虚拟地址映射到相同的物理地址上,NCNB(不使用cache,不使用write buffer)——这样,对寄存器的操作跟未启动MMU时是一样的;再将SDRAM对应的64M空间的页表项修改为使用cache。
mem_mapping_linear函数的代码在arch/s3c2410/mmu.c中:[main(int argc, char *argv[]) > mem_map_init(void) >mem_map_nand_boot( ) > mem_mapping_linear(void)]1 static inline void mem_mapping_linear(void)2 {3 unsigned long pageoffset, sectionNumber;4 putstr_hex("MMU table base address = 0x", (unsignedlong)mmu_tlb_base);5 /* 4G 虚拟地址映射到相同的物理地址. not cacacheable, not bufferable */6 /* mmu_tlb_base = 0x33dfc000*/7 for (sectionNumber = 0; sectionNumber < 4096; sectionNumber++) {8 pageoffset = (sectionNumber << 20);9 *(mmu_tlb_base + (pageoffset >> 20)) = pageoffset |MMU_SECDESC;10 }11 /* make dram cacheable */12 /* SDRAM物理地址0x3000000-0x33ffffff,13 DRAM_BASE=0x30000000,DRAM_SIZE=64M14 */15 for (pageoffset = DRAM_BASE; pageoffset < (DRAM_BASE+DRAM_SIZE); \16 pageoffset += SZ_1M) {17 //DPRINTK(3, "Make DRAM section cacheable: 0x%08lx\n", pageoffset);18 *(mmu_tlb_base + (pageoffset >> 20)) = \pageoffset | MMU_SECDESC | MMU_CACHEABLE;19 }20 }mmu_init()函数用于启动MMU,它直接调用arm920_setup()函数。