收藏 分享(赏)

uboot启动代码详解.doc

上传人:精品资料 文档编号:9447708 上传时间:2019-08-08 格式:DOC 页数:50 大小:540KB
下载 相关 举报
uboot启动代码详解.doc_第1页
第1页 / 共50页
uboot启动代码详解.doc_第2页
第2页 / 共50页
uboot启动代码详解.doc_第3页
第3页 / 共50页
uboot启动代码详解.doc_第4页
第4页 / 共50页
uboot启动代码详解.doc_第5页
第5页 / 共50页
点击查看更多>>
资源描述

1、1 引言在专用的嵌入式板子运行 GNU/Linux 系统已经变得越来越流行。一个嵌入式 Linux 系统从软件的角度看通常可以分为四个层次:1. 引导加载程序。固化在固件(firmware)中的 boot 代码,也就是 Boot Loader,它的启动通常分为两个阶段。2. Linux 内核。特定于嵌入式板子的定制内核以及内核的启动参数。3. 文件系统。 包括根文件系统和建立于 Flash 内存设备之上文件系统,root fs。4. 用户应用程序。 特定于用户的应用程序。有时在用户应用程序和内核层之间可能还会包括一个嵌入式图形用户界面。常用的嵌入式 GUI 有:MicroWindows 和 M

2、iniGUI 等。引导加载程序是系统加电后运行的第一段软件代码。回忆一下 PC 的体系结构我们可以知道,PC 机中的引导加载程序由 BIOS(其本质就是一段固件程序) 和位于硬盘 MBR 中的 OS Boot Loader(比如,LILO 和 GRUB 等)一起组成。BIOS 在完成硬件检测和资源分配后,将硬盘 MBR 中的 Boot Loader 读到系统的 RAM 中,然后将控制权交给 OS Boot Loader。Boot Loader 的主要运行任务就是将内核映象从硬盘上读到 RAM 中,然后跳转到内核的入口点去运行,也即开始启动操作系统。而在嵌入式系统中,通常并没有像 BIOS 那样

3、的固件程序(注,有的嵌入式 CPU 也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由 Boot Loader 来完成。比如在一个基于 ARM7TDMI core 的嵌入式系统中,系统在上电或复位时通常都从地址 0x00000000 处开始执行,而在这个地址处安排的通常就是系统的 Boot Loader 程序。2 bootloader 简介应 用 程 序文 件 系 统操 作 系 统 内 核BootLoader简单地说,Boot Loader (引导加载程序)就是在操作系统内核运行之前运行的一段小程序,它的作用就是加载操作系统,它是系统加电后运行的第一段软件代码。通过这段代码实现硬

4、件的初始化,建立内存空间的映射图,为操作系统内核准备好硬件环境并引导内核的启动。如上图所示的那样在设备的启动过程中 bootloader 位于最底层,首先被运行来引导操作系统运行,很容易可以看出 bootloader 是底层程序所以它的实现严重地依赖于硬件,特别是在嵌入式世界。因此,在嵌入式世界里建立一个通用的 BootLoader 几乎是不可能的。尽管如此,一些功能强大、支持硬件环境较多的 BootLoader 也被广大的使用者和爱好者所支持,从而形成了一些被广泛认可的、较为通用的的 bootloader 实现。2.1 Boot Loader 所支持的 CPU 和嵌入式板每种不同的 CPU

5、体系结构都有不同的 Boot Loader。有些 Boot Loader 也支持多种体系结构的 CPU,比如 U-Boot 就同时支持 ARM 体系结构和 MIPS 体系结构。除了依赖于 CPU 的体系结构外,Boot Loader 实际上也依赖于具体的嵌入式板级设备的配置。这也就是说,对于两块不同的嵌入式板而言,即使它们是基于同一种 CPU 而构建的,要想让运行在一块板子上的 Boot Loader 程序也能运行在另一块板子上,通常也都需要修改 Boot Loader 的源程序。2.2 Boot Loader 的安装媒介(Installation Medium)系统加电或复位后,所有的 CP

6、U 通常都从某个由 CPU 制造商预先安排的地址上取指令。比如,基于 ARM7TDMI core 的 CPU 在复位时通常都从地址 0x00000000 取它的第一条指令。而基于 CPU 构建的嵌入式系统通常都有某种类型的固态存储设备( 比如:ROM、 EEPROM 或 FLASH 等)被映射到这个预先安排的地址上。因此在系统加电后,CPU 将首先执行 Boot Loader 程序。下图 1 就是一个同时装有 Boot Loader、内核的启动参数、内核映像和根文件系统映像的固态存储设备的典型空间分配结构图:图 1 固态存储设备的典型空间分配结构2.3 Boot Loader 的启动过程:单阶

7、段(Single Stage)/多阶段(Multi-Stage)通常多阶段的 Boot Loader 能提供更为复杂的功能,以及更好的可移植性。从固态存储设备上启动的 Boot Loader 大多都是 2 阶段的启动过程,也即启动过程可以分为 stage 1 和 stage 2 两部分。而至于在 stage 1 和 stage 2 具体完成哪些任务将在下面讨论。2.4 Boot Loader 的操作模式 (Operation Mode)大多数 Boot Loader 都包含两种不同的操作模式:“ 启动加载“模式和“ 下载“模式,这种区别仅对于开发人员才有意义。但从最终用户的角度看,Boot L

8、oader 的作用就是用来加载操作系统,而并不存在所谓的启动加载模式与下载工作模式的区别。启动加载(Boot loading)模式:这种模式也称为“自主“(Autonomous)模式。也即 Boot Loader 从目标机上的某个固态存储设备上将操作系统加载到 RAM 中运行,整个过程并没有用户的介入。这种模式是 Boot Loader 的正常工作模式,因此在嵌入式产品发布的时侯,Boot Loader 显然必须工作在这种模式下。下载(Downloading)模式:在这种模式下,目标机上的 Boot Loader 将通过串口连接或网络连接等通信手段从主机(Host)下载文件,比如:下载内核映像

9、和根文件系统映像等。从主机下载的文件通常首先被 Boot Loader 保存到目标机的 RAM 中,然后再被 Boot Loader 写到目标机上的 FLASH 类固态存储设备中。Boot Loader 的这种模式通常在第一次安装内核与根文件系统时被使用;此外,以后的系统更新也会使用 Boot Loader 的这种工作模式。工作于这种模式下的 Boot Loader 通常都会向它的终端用户提供一个简单的菜单界面或命令行接口来接收要执行的操作。像 Blob 或 U-Boot 等这样功能强大的 Boot Loader 通常同时支持这两种工作模式,而且允许用户在这两种工作模式之间进行切换。比如,Bl

10、ob 在启动时处于正常的启动加载模式,但是它会延时 10 秒等待终端用户按下任意键而将 blob 切换到下载模式。如果在 10 秒内没有用户按键,则 blob 继续启动 Linux 内核。2.5 常见的 Boot LoaderU-BOOT:U-Boot 是 Das U-Boot 的简称,其含义是 Universal Boot Loader,是遵循 GPL 条款的开放源码项目。uboot 是一个庞大的公开源码的软件。它支持一些系列的 arm 体系,包含常见的外设的驱动,是一个功能强大的板极支持包。vivi:vivi 是韩国 mizi 公司开发的 bootloader, 适用于 ARM9 处理器。

11、 Vivi 也有两种工作模式:启动加载模式和下载模式。启动加载模式可以在一段时间后(这个时间可更改)自行启动 linux 内核,这是 vivi 的默认模式。如果修改或更新需要进入下载模式,在下载模式下,vivi 为用户提供一个命令行接口通过接口可以使用 vivi 提供的一些命令,来实现flash 的烧写、管理、操作 mtd 分区信息、启动系统等功能。2.6 U-BOOT 的目录结构目录 说明board 和开发板相关的文件,每一个开发板都以一个子目录出现在当前目录中,比如:smdk2410。该子目录中存放于开发板相关的配置文件,如makefile 和 U-Boot.lds。其中包含 SDRAM

12、初始化代码、Flash 底层驱动、板级初始化文件。config.mk 定义了 TEXT_BASE 是代码在内存的真实地址common 与体系结构无关的文件,实现各种命令的 C 文件。该文件主要实现uboot 命令行下支持的命令,每一条命令都对应一个文件。例如 bootm命令对应就是 cmd_bootm.c。cpu 与特定 CPU 架构相关目录,每一款 uboot 下支持的 CPU 在该目录下对应一个子目录,比如 arm920t。每个 CPU 子目录中都包括 cpu.c 和interrupt.c、start.S。cpu.c 初始化 CPU、设置指令 Cache 和数据 Cache等 interr

13、upt.c 设置系统的各种中断和异常 start.S 是 U-boot 启动时执行的第一个文件,它主要做早期系统初始化,代码重定向和设置系统堆栈disk Disk 分区处理代码,对磁盘的支持doc 文档目录,uboot 有非常完整的文档drivers Uboot 支持的设备驱动程序都放在该目录,例如各种网卡、支持 CFI的 Flash、串口、USB 等fs 支持的文件系统,目前支持 cramfs、fat、fdos、jffs2 和 registerfsinclude 头文件,还有对各种硬件平台支持的汇编文件,系统配置文件和对文件系统支持的文件等。该目录下的 configs 目录有与开发板相关的配

14、置文件,如 smdk2410.h。该目录下的 asm 目录有与 CPU 体系结构相关的头文件,比如 arm 对应的就是 asm-arm。net 与网络协议栈相关的代码,BOOTP 协议、TFTP 协议、RARP 和 NFS文件系统的实现等lib_xxx 与 ARM 体系结构相关的库文件。如与 arm 相关的库放在 lib_arm 中tools 生成 uboot 的工具,如 mkimage,crc 等等3 Boot Loader 的主要任务与典型结构框架从操作系统的角度看,Boot Loader 的总目标就是正确地调用内核来执行。另外,由于 Boot Loader 的实现依赖于 CPU 的体系结

15、构,因此大多数 Boot Loader 都分为 stage1 和 stage2 两大部分。依赖于 CPU 体系结构的代码,比如设备初始化代码等,通常都放在 stage1 中,而且通常都用汇编语言来实现,以达到短小精悍的目的。而 stage2 则通常用 C 语言来实现,这样可以实现给复杂的功能,而且代码会具有更好的可读性和可移植性。以 u-boot 为例,它启动过程的两个阶段(stage) 如下:第一阶段(stage 1) cpu/arm920t/start.S依赖于 CPU 体系结构的代码(如设备初始化代码等) ,一般用汇编语言来实现。主要进行以下方面的设置:设置 ARM 进入 SVC 模式、

16、禁止 IRQ 和 FIQ、关闭看门狗、屏蔽所有中断。设置时钟(FCLK,HCLK,PCLK)、清空 I/D cache、清空 TLB、禁止 MMU 和cache、配置内存控制器、为搬运代码做准备、搬移 uboot 映像到 RAM 中(使用 copy_loop实现) 、分配堆栈、清空 bss 段(使用 clbss_l 实现) 。最后通过 ldr pc, _start_armboot 跳转到第二阶段。第二阶段(stage 2) lib_arm/board.c该阶段主要都是用语言来实现。start_armboot()进行一系列初始化(cpu, 板卡,中断,串口,控制台等) ,开启 I/D cache

17、。初始化 FLASH,根据系统配置执行其他初始化操作。打印 LOG,使能中断,获取环境变量,初始化网卡。最后进入 main_loop()函数。综上所述,可简单的归纳两个阶段的功能如下:第一阶段的功能: 硬件设备初始化 加载 U-Boot 第二阶段代码到 RAM 空间 设置好栈 跳转到第二阶段代码入口第二阶段的功能: 初始化本阶段使用的硬件设备 检测系统内存映射 将内核从 Flash 读取到 RAM 中 为内核设置启动参数 调用内核U-Boot 启动第一阶段流程如下:3.1 u-boot 的 stage1 详细分析uboot 的第一阶段设计的非常巧妙,几乎都是用汇编语言实现的。首先我们来看一下它

18、的链接脚本(u-boot-1.1.6boardsmdk2410u-boot.lds),通过它我们可以知道它整个程序的各个段是怎么存放的。它定义了整个程序编译之后的连接过程,决定了一个可执行程序的各个段的存储位置。/* 指定输出可执行文件是 elf 格式,32 位 ARM 指令,小端 */OUTPUT_FORMAT(“elf32-littlearm“, “elf32-littlearm“, “elf32-littlearm“)/* 指定输出可执行文件的平台架构为 ARM 架构 */OUTPUT_ARCH(arm)/* 指定输出可执行文件的起始代码段为_start */ENTRY(_start)S

19、ECTIONS. = 0x00000000;/入口地址. = ALIGN(4);/四字节对齐.text :/代码段,上面 3 行标识是不占任何空间的cpu/arm920t/start.o (.text)/这里将 start.o 放在第一位就表示把 start.s 编译时放在最开始,也就是 uboot 启动是最先执行 start.S*(.text)/所有的其他程序的代码段以四字节对齐放在它后面. = ALIGN(4); /前面的“.”表示当前值.rodata : *(.rodata) /只读数据段. = ALIGN(4);.data : *(.data) /指定读/写数据段. = ALIGN(4

20、);.got : *(.got) /指定 got 段,got 段式是 uboot 自定义的一个段,非标准段. = .;_u_boot_cmd_start = .;/把_u_boot_cmd_start 赋值为当前位置,即起始位置.u_boot_cmd : *(.u_boot_cmd) /指定 u_boot_cmd 段,uboot 把所有的 uboot 命令放在该段_u_boot_cmd_end = .;/把 _u_boot_cmd_end 赋值为当前位置,即结束位置. = ALIGN(4);_bss_start = .;/_bss_start 赋值为当前位置,即 bss 段得开始位置.bss

21、: *(.bss) _end = .;/把_end 赋值为当前位置,即 bss 段得结束地址从上面这段代码我们可以看出 uboot 运行的第一个程序是 cpu/arm920t/start.S 里面的第一个段_start。我们查看 start.S 的源码。3.1.1 硬件设备初始化(1)设置异常向量cpu/arm920t/start.S 开头有如下的代码:/global 用于声明一个符号可被其他文档引用,相当于声明了一个全局变量,.globl 和.global 相同。/该部分为处理器的异常处理向量表。地址范围为 0x00000000 0x00000020,刚好 8 条指令。.globl _sta

22、rt /* u-boot 启动入口 */_start: b reset /* 复位 */ldr pc, _undefined_instruction /* 未定义指令向量 */ldr pc, _software_interrupt /* 软件中断向量 */ldr pc, _prefetch_abort /* 预取指令异常向量 */ldr pc, _data_abort /* 数据操作异常向量 */ldr pc, _not_used /* 未使用 */ldr pc, _irq /* irq 中断向量 */ldr pc, _fiq /* fiq 中断向量 */* 中断向量表入口地址 */.word

23、 伪操作用于分配一段字内存单元(分配的单元都是字对齐的),并用伪操作中的expr 初始化。.long 和.int 作用与之相同。_undefined_instruction: .word undefined_instruction_software_interrupt: .word software_interrupt_prefetch_abort: .word prefetch_abort_data_abort: .word data_abort_not_used: .word not_used_irq: .word irq_fiq: .word fiq.balignl 16,0xdeadb

24、eef 以上代码设置了 ARM 异常向量表,各个异常向量介绍如下:地址 异常 进入模式 描述0x00000000 复位 管理模式 复位电平有效时,产生复位异常,程序跳转到复位处理程序处执行0x00000004 未定义指令 未定义模式 遇到不能处理的指令时产生未定义指令异常0x00000008 软件中断 管理模式 执行 SWI 指令产生,用于用户模式下的程序调用特权操作指令0x0000000c 预存指令 中止模式 处理器预取指令的地址不存在,或该地址不允许当前指令访问,产生指令预取中止异常0x00000010 数据操作 中止模式 处理器数据访问指令的地址不存在或该地址不允许当前指令访问时,产生数

25、据中止异常0x00000014 未使用 未使用 未使用0x00000018 IRQ IRQ 外部中断请求有效,且 CPSR 中的 I 位为 0时,产生 IRQ 异常0x0000001c FIQ FIQ 快速中断请求引脚有效,且 CPSR 中的 F 位为 0 时,产生 FIQ 异常在 cpu/arm920t/start.S 中还有这些异常对应的异常处理程序。当一个异常产生时,CPU根据异常号在异常向量表中找到对应的异常向量,然后执行异常向量处的跳转指令,CPU就跳转到对应的异常处理程序执行。其中复位异常向量的指令“b reset”决定了 U-Boot 启动后将自动跳转到标号 reset 处执行。

26、许多人都认为_start 的值是 0x00000000,为什么是这个地址呢? 因为连接脚本上指定了。真的是这样吗?我们来看看我们编译好之后,在 u-boot 目录下有个 System.map,这里面有各个变量的值,其中会告诉你,_start 的值为:0x33f80000。注意,这里有两个地址:编译地址和运行地址。什么是编译地址?什么是运行地址? 32 位的处理器,它的每一条指令是 4 个字节,以 4 个字节存储顺序,进行顺序执行,CPU 是顺序执行的,只要没发生什么跳转,它会顺序进行执行,编译器会对每一条指令分配一个编译地址,这是编译器分配的,在编译过程中分配的地址,我们称之为编译地址。运行地

27、址是指,程序指令真正运行的地址,是由用户指定的,用户将运行地址烧录到哪里,哪里就是运行的地址。编译地址和运行地址如何来算呢?假如有两个编译地址a=0x10,b=0x7,b 的运行地址是 0x300 ,那么 a 的运行地址就是 b 的运行地址加上两者编译地址的差值,a-b=0x10-0x7=0x3 ,a 的运行地址就是 0x300+0x3=0x303。(2)CPU 进入 SVC 模式start_code:/* set the cpu to SVC32 mode*/mrs r0, cpsrbic r0, r0, #0x1f /*工作模式位清零 */orr r0, r0, #0xd3 /*工作模式位

28、设置为“10011”(管理模式),并将中断禁止位和快中断禁止位置 1 */msr cpsr, r0以上代码将 CPU 的工作模式位设置为管理模式,并将中断禁止位和快中断禁止位置一,从而屏蔽了 IRQ 和 FIQ 中断。(3)设置控制寄存器地址#if defined(CONFIG_S3C2400)#define pWTCON 0x15300000#define INTMSK 0x14400008#define CLKDIVN 0x14800014#else /* s3c2410 与 s3c2440 下面 4 个寄存器地址相同 */#define pWTCON 0x53000000 /* WATC

29、HDOG 控制寄存器地址 */#define INTMSK 0x4A000008 /* INTMSK 寄存器地址 */#define INTSUBMSK 0x4A00001C /* INTSUBMSK 寄存器地址 */#define CLKDIVN 0x4C000014 /* CLKDIVN 寄存器地址 */# endif对于 s3c2440 开发板,以上代码完成了WATCHDOG,INTMSK,INTSUBMSK,CLKDIVN 四个寄存器的地址的设置。(4)关闭看门狗ldr r0, =pWTCONmov r1, #0x0str r1, r0 /* 看门狗控制器的最低位为 0 时,看门狗不输

30、出复位信号 */以上代码向看门狗控制寄存器写入 0,关闭看门狗。为什么需要关闭看门狗呢?这里有个喂狗的过程,所谓的喂狗是每隔一段时间给某个寄存器置位而已,在实际中会专门启动一个线程或进程会专门喂狗,当上层软件出现故障时就会停止喂狗,停止喂狗之后,cpu会自动复位,一般都在外部专门有一个看门狗,做一个外部的电路,不在 cpu 内部使用看门狗,否则在 U-Boot 启动过程中,CPU 将不断重启。(5)屏蔽中断/* mask all IRQs by setting all bits in the INTMR - default*/mov r1, #0xffffffff /* 某位被置 1 则对应的

31、中断被屏蔽 */ldr r0, =INTMSKstr r1, r0INTMSK 是主中断屏蔽寄存器,每一位对应 SRCPND(中断源引脚寄存器)中的一位,表明 SRCPND 相应位代表的中断请求是否被 CPU 所处理。INTMSK 寄存器是一个 32 位的寄存器,每位对应一个中断,向其中写入 0xffffffff 就将 INTMSK 寄存器全部位置 1,从而屏蔽对应的中断。为什么要关闭中断呢?中断处理(ldr pc )是将代码的编译地址放在了指针上,而这段时间内还没有搬移代码,所以不能进行跳转。#if defined(CONFIG_S3C2440)ldr r1, =0x7fffldr r0,

32、=INTSUBMSKstr r1, r0#endifINTSUBMSK 每一位对应 SUBSRCPND 中的一位,表明 SUBSRCPND 相应位代表的中断请求是否被 CPU 所处理。INTSUBMSK 寄存器是一个 32 位的寄存器,但是只使用了低 15 位。向其中写入 0x7fff 就是将 INTSUBMSK 寄存器全部有效位(低 15 位)置 1,从而屏蔽对应的中断。(6)设置 MPLLCON,UPLLCON, CLKDIVN#if defined(CONFIG_S3C2440)#define MPLLCON 0x4C000004#define UPLLCON 0x4C000008ldr

33、 r0, =CLKDIVNmov r1, #5str r1, r0ldr r0, =MPLLCONldr r1, =0x7F021str r1, r0ldr r0, =UPLLCONldr r1, =0x38022str r1, r0#else/* FCLK:HCLK:PCLK = 1:2:4 */* default FCLK is 120 MHz ! */ldr r0, =CLKDIVNmov r1, #3str r1, r0#endifCPU 上电几毫秒后,晶振输出稳定,FCLK=Fin(晶振频率) ,CPU 开始执行指令。但实际上,FCLK 可以高于 Fin,为了提高系统时钟,需要用软件

34、来启用 PLL。这就需要设置CLKDIVN,MPLLCON,UPLLCON 这 3 个寄存器。CLKDIVN 寄存器用于设置 FCLK,HCLK,PCLK 三者间的比例如下:CLKDIVN 位 说明 初始值HDIVN 2:1 00 : HCLK = FCLK/1.01 : HCLK = FCLK/2.10 : HCLK = FCLK/4 (当 CAMDIVN9 = 0 时)HCLK= FCLK/8 (当 CAMDIVN9 = 1 时)11 : HCLK = FCLK/3 (当 CAMDIVN8 = 0 时)HCLK = FCLK/6 (当 CAMDIVN8 = 1 时)00PDIVN 0 0:

35、 PCLK = HCLK/1 1: PCLK = HCLK/2 0设置 CLKDIVN 为 5,就将 HDIVN 设置为二进制的 10,由于 CAMDIVN9没有被改变过,取默认值 0,因此 HCLK = FCLK/4。PDIVN 被设置为 1,因此 PCLK= HCLK/2。因此分频比 FCLK:HCLK:PCLK = 1:4:8 。MPLLCON 寄存器用于设置 FCLK 与 Fin 的倍数。MPLLCON 的位19:12称为MDIV,位9:4 称为 PDIV,位1:0称为 SDIV。对于 S3C2440,FCLK 与 Fin 的关系如下面公式:MPLLCON 与 UPLLCON 通常设置

36、如下:输入频率 输出频率 MDIV PDIV SDIV12.0000MHz 48.00 MHz 56(0x38) 2 212.0000MHz 405.00 MHz 127(0x7f) 2 1当 s3c2440 系统主频设置为 405MHZ,USB 时钟频率设置为 48MHZ 时,系统可以稳定运行,因此设置 MPLLCON 与 UPLLCON 为:MPLLCON=(0x7fLOCKTIME = 0xFFFFFF;/* configure MPLL 配置 MPLL */clk_power-MPLLCON = (M_MDIV UPLLCON = (U_M_MDIV GPACON = 0x007FFF

37、FF;gpio-GPBCON = 0x00044555;gpio-GPBUP = 0x000007FF;gpio-GPCCON = 0xAAAAAAAA;gpio-GPCUP = 0x0000FFFF;gpio-GPDCON = 0xAAAAAAAA;gpio-GPDUP = 0x0000FFFF;gpio-GPECON = 0xAAAAAAAA;gpio-GPEUP = 0x0000FFFF;gpio-GPFCON = 0x000055AA;gpio-GPFUP = 0x000000FF;gpio-GPGCON = 0xFF95FFBA;gpio-GPGUP = 0x0000FFFF;gpi

38、o-GPHCON = 0x002AFAAA;gpio-GPHUP = 0x000007FF;/* SMDK2410 开发板的机器码 */gd-bd-bi_arch_number = MACH_TYPE_SMDK2410;/* adress of boot parameters 内核启动参数地址,运行时在 linux 内核之下 */gd-bd-bi_boot_params = 0x30000100;/* 使能指令 cache 和数据 cache */icache_enable();dcache_enable();return 0;函数 icache_enable 和 dcache_enable,

39、定义在 cpu/arm920t/cpu.c 中,这两个函数是通过修改 CP15 的 c1 寄存器来实现的,使能 cache 很简单,只要把协处理器 15 的相关位打开就行了,这里来是将 c1 的 I、C 位置 1,来开启 Icache、DCaches。我这里只分析icache_enable,dcache_enable 类似。icache_enable 具体实现如下:void icache_enable (void)ulong reg;reg = read_p15_c1 (); /* get control reg. 获取 CP15 的 c1 寄存器值存到 reg 中*/cp_delay ();

40、write_p15_c1 (reg | C1_IC); /*这里将 C1 寄存器的 I、C 位置 1,来开启Icache、Dcaches*/这里须要理解的是 read_p15_c1与 write_p15_c1函数,它们分别在 cpu/arm920t/cpu.c 中定义如下:static unsigned long read_p15_c1 (void)unsigned long value;_asm_ _volatile_(“mrc p15, 0, %0, c1, c0, 0 read control regn“ /* %0 是参数传递时 R0 寄存器,其功能是读取 CP15 的 c1 寄存器值

41、放到 r0 中*/: “=r“ (value): “memory“);#ifdef MMU_DEBUGprintf (“p15/c1 is = %08lxn“, value);#endifreturn value; 返回读取 CP15 的 c1 寄存器的值/* write to co-processor 15, register #1 (control register) */static void write_p15_c1 (unsigned long value)#ifdef MMU_DEBUGprintf (“write %08lx to p15/c1n“, value);#endif_

42、asm_ _volatile_(“mcr p15, 0, %0, c1, c0, 0 write it backn“ 保存 r0 的值到控制寄存器CP15 的 c1 寄存器中,因为函数参数传递时,第一个参数都是放在 r0 寄存器中的。: “r“ (value): “memory“);read_p15_c1 ();interrupt_init 函数:cpu/arm920t/s3c24x0/interrupts.cint timer_load_val = 0;static ulong timestamp;static ulong lastdec;int interrupt_init (void)

43、/* 初始化 timer4 相关寄存器,用于产生 10ms 定时中断信号 */* 返回定时器配置寄存器地址 0x51000000,即 TCFG0 寄存器地址 */S3C24X0_TIMERS * const timers = S3C24X0_GetBase_TIMERS();/* 这里使用定时器 4,定时器 4 有只有一个内部定器没有输出管脚 */* prescaler for Timer 4 is 16 */timers-TCFG0 = 0x0f00;if (timer_load_val = 0)timer_load_val = get_PCLK()/(2 * 16 * 100); /* g

44、et_PCLK 返回 PCLK 频率 */* load value for 10 ms timeout */lastdec = timers-TCNTB4 = timer_load_val; /* 设置计数缓存寄存器初始值 */* 设置定时器 4 手动更新,自动加载模式,并关闭定时器 4 */timers-TCON = (timers-TCON /* auto load, 启动 Timer 4 */timers-TCON = (timers-TCON timestamp = 0;return (0);对着 datasheet 来看这个函数, 实际上这个函数使用 timer 4来作为系统 clo

45、ck, 即时钟滴答, 10ms 一次,到点就产生一个中断,但由于此时中断还没打开所以这个中断不会响应。env_init 函数: 由于我们在 inculde/configs/smdk2410.h 下定义了 CFG_ENV_IS_IN_FLASH因此该函数位于 common/env_flash.c 下int env_init(void)#ifdef CONFIG_OMAP2420H4int flash_probe(void);if(flash_probe() = 0)goto bad_flash;#endifif (crc32(0, env_ptr-data, ENV_SIZE) = env_pt

46、r-crc) gd-env_addr = (ulong)gd-env_valid = 1; /* 使用 include/configs/smdk2410.h 配置的环境变量则设置环境变量可用标志 */return(0);/* env_ptr 在前面定义为 env_t *env_ptr = (env_t *)CFG_ENV_ADDR;而CFG_ENV_ADDR 被定义在 include/configs/smdk2410.h 中了,这里判断如果校验正确( 即CFG_ENV_ADDR 被定义了) 则环境变量的存放地址使用 smdk2410.h 中定义的,否则使用后面的默认的环境变量值 default

47、_environment 数组*/#ifdef CONFIG_OMAP2420H4bad_flash:#endifgd-env_addr = (ulong)gd-env_valid = 0; 使用默认的环境配置变量则设置环境变量不可用标志return (0);这个函数主要是在 gd 里保存环境变量的存放地址。一般使用默认的环境变量值即default_environment 数组,它在 common/env_commom.c 中定义如下:uchar default_environment = #ifdef CONFIG_BOOTARGS“bootargs=“ CONFIG_BOOTARGS “0

48、“#endif#ifdef CONFIG_BOOTCOMMAND“bootcmd=“ CONFIG_BOOTCOMMAND “0“#endif#if defined(CONFIG_BOOTDELAY) 可见环境变量以如下的方式存放在数组中:Name=value,并且以 ”/0”结束, 而类似CONFIG_BOOTARGS 的宏都定义在板子自己的配置文件中即 smdk2410.h 里。init_baudrate 函数:lib_arm/board.cstatic int init_baudrate (void)char tmp64; /* long enough for environment v

49、ariables */int i = getenv_r (“baudrate“, tmp, sizeof (tmp); /* 从环境变量中获取波特率值 */gd-bd-bi_baudrate = gd-baudrate = (i 0)? (int) simple_strtoul (tmp, NULL, 10): CONFIG_BAUDRATE;return (0);该函数从上面刚初始化好的环境变量列表里找波特率值,如果没有就赋初始值为CONFIG_BAUDRATE。serial_init 函数: cpu/arm920t/s3c24x0/serial.cint serial_init (void)serial_setbrg (); /* 设置波特率,停止位等 */return (0);void serial_setbrg (void)S3C24X0_UART * const uart = S

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报