从零开始的uboot系统移植5
---uboot源码分析第一部分start.s上
本总结基于朱有鹏嵌入式大讲坛uboot的移植
我觉得了解uboot最好的方式是学习uboot的源代码0.02,如果你认真的阅读的话,那么你就会发现
以前的uboot写的是非常的“简单”,所有的配置文件都是仅仅用一个条件编译来编写的而且细心的人可以看
到最初的版本仅仅只支持S3C2440和S3c2410,没有所谓的BSS段,到后面uboot越来越大,有了BSS段了,
所有的配置需要使用链接文件了?
为什么,因为每一个开发板都需要有其支持的东西啊,这样我们就可以一个uboot程序可以支持很多开发板啊。
BSS段:
BSS段是用来存放一些未初始化的全局变量等,为啥要放在这里啊?
因为放在一个地方,一下子全部把它全部初始化不好吗?
为什么我们要这样啊?因为uboot是裸机程序啊,并没有操作系统帮你管理内存,
需要你自己去管理内存。BSS段,只读数据段,数据段,代码段,
代码是用来加工数据的!!!
所有版本的u-boot源代码压缩包都可以在ftp://ftp.denx.de/pub/u-boot/下载。
关于u-boot源代码的信息,看http://www.denx.de/wiki/U-Boot/SourceCode
1、阶段的定义:
第一阶段:
即在内部SRAM运行的阶段,简单的理解为汇编阶段,此阶段主要涉及start.s文件,
在CPU/s5pc11x/目录下面。
第一阶段以ldr pc _start_armboot
第二阶段:
即在DDR中运行的阶段,简单的理解为C语言阶段,此阶段主要涉及start_armboot函数,
在uboot/lib_arm/board.c文件的444到908行
2、总结第一阶段的主要完成的任务
2.1、异常向量表的实现;
2.2、设置进入特权模式,即SVC模式;
2.3、检查恢复状态;
2.4、IO状态恢复;
2.5、关闭看门狗;
2.6、一些SRAM,SROM相关的GPIO设置;
2.7、开发板的供电锁存
2.8、时钟的初始化
2.9、DDR的初始化;
2.10、重新设置栈空间;
2.11、uboot的重定位;
2.12、转换表的建立
2.13、使能MMU
3、start.s的分析部分1
#include <config.h>
#include <version.h>
#if defined(CONFIG_ENABLE_MMU)
#include <asm/proc/domain.h>
#endif
#include <regs.h>
(1)config.h文件在mkconfig脚本中生成,此文件的内容为#include <configs/x210.h>
(2)Version.h文件中的内容是#include “version_autogeneratied.h”
(3)version_autogeneratied.h生成的代码为#define U_BOOT_VERSION “U-boot 1.3.4”
(4)由于定义了宏CONFIG_ENABLE_MMU,因此包含asm/proc/domain.h,实际包含include/asm-arm/proc-arm/domain.h文件。
4、start.s的解析2
(1)启动代码的16字节头部:
#if defined(CONFIG_EVT1) && !Defined (CONFIG_FUSED)
.word 0x2000
.word 0x0000
.word 0x0000
.word 0x0000
#endif
1、arm7和arm9的指令集,一个字的类型是32bit
2.word是arm汇编的伪指令,含义是当前地址的值为XX,XX的内容一般为数值或者地址。
如:.word 0x2000表示当前地址的值为0x2000;.word _start 表示当前地址的值为_start。
(2)构建异常向量表
.globl _start
_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
ldr pc, _fiq
_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,0xdeadbeef
1、异常向量表由硬件决定,软件只是参照硬件设计来实现。
2、此向量表只是做到了其中的一点点的部分,并没有做到非常细致的异常处理
3、复位异常处理的代码是reset是一个函数 b reset
4、上图中的最后一句是让内存以16字节来对齐,如果不对齐,用oxdeadbeef这个数字填充
5、学完裸机中的中断向量表可知,这里有类似的操作。
总结:
所谓的异常向量表,听起来好像很牛逼的样子,但是其实就是你的这个程序跑飞了,然后
会进入什么样的状态而已,但是如果没有这个异常状态表,你就很难去跟踪你的程序到底是
哪里出错了,这个中断向量表,这里的异常向量表也包含了中断模式和快速中断的模式;
未定义
软件中断
预处理的指令
数据出错
未使用的代码
普通中断
快速中断
这些异常的模式都代表一条公路,可以同往该去的地方,后面的.word就是对应的地址的代码。
(前面的一段里面有说明)
(3)一些重要的地址值:
_TEXT_BASE:
.word TEXT_BASE
.globl _armboot_start 这里相当于定义了一个全局变量_armboot_start
_armboot_start:
.word _start
/*
* Note: _armboot_end_data and _armboot_end are defined
* by the (board-dependent) linker script.
* _armboot_end_data is the first usable FLASH address after armboot
*/
.globl _armboot_end_data
_armboot_end_data:
.word armboot_end_data
.globl _armboot_end
_armboot_end:
.word armboot_end
/*
* _armboot_real_end is the first usable RAM address behind armboot
* and the various stacks
*/
.globl _armboot_real_end
_armboot_real_end:
.word 0x0badc0de
//这里的代码主要定义了两个配置文件,如果定义了中断,那么就需要用到中断的堆栈,否则不能使用中断。
#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
1、_TEXT_BASE(4字节)这个内存地址出的值为TEXT_BASE(即0xc3e00000)。
2、_TEXT_PHY_BASE(4字节)这个内存存放的值为CFG_PHY_UBOOT_BASE(uboot的物理基地址)。
3、CFG_PHY_UBOOT_BASE(定义在x210_sd.h中)为 MEMORY_BASE_ADDRESS + 0x3e00000
在x210_sd.h中有#define MEMORY_BASE_ADDRESS 0x30000000;
因此CFG_PHY_UBOOT_BASE为0x33e00000,这个是uboot在DDR中的物理地址。
4、总结,即标签表示地址,.word后面的表示此内存地址中存储的值。
如_bss_end: .word _end表示地址_bss_end上存放的值是_end。
4、start.s解析3
(1)reset中断处理函数
/*
* the actual reset code
*/
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
设置寄存器为0xd3的时候,则CPU设置为SVC模式,arm状态,禁止FIQ,IRQ中断。
整个uboot的工作时,CPU一直处于SVC模式。
以下的代码使用的是uboot最早的版本,所以肯定是不完善写的不够好,牛逼的程序,
一般是没有注释的,为什么呢?因为是牛逼的人写的,他认为我们都是懂的,其实我们是
不懂的
(2)关闭看门狗:
此代码主要使用了两个配置选项来选择2440或者2410的代码,这里是uboot最早的代码,
还是十分的简单,没有支持很多的开发板但是已经有配置的思想了。
/* turn off the watchdog */
#if defined(CONFIG_S3C2400)
#define pWTCON 0x15300000
/* Interupt-Controller base addresses */
#define INTMSK 0x14400008
/* clock divisor register */
#define CLKDIVN 0x14800014
#elif defined(CONFIG_S3C2410)
#define pWTCON 0x53000000
/* Interupt-Controller base addresses */
#define INTMSK 0x4A000008
#define INTSUBMSK 0x4A00001C
/* clock divisor register */
#define CLKDIVN 0x4C000014
#endif
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
#if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
#endif
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
#ifdef CONFIG_INIT_CRITICAL
bl cpu_init_crit
#endif
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
(3)CPU的初始化的代码:
不用问为什么这么写,没有什么为什么,因为它的用户手册已经规定了使用什么寄存器,
寄存器的地址是什么了。
cpu_init_crit:
/*
* flush v4 I/D caches
*/
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
/*
* disable MMU stuff and caches
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
orr r0, r0, #0x00000002 @ set bit 2 (A) Align
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
mcr p15, 0, r0, c1, c0, 0
/*
* before relocating, we have to setup RAM timing
* because memory timing is board-dependend, you will
* find a memsetup.S in your board directory.
*/
mov ip, lr
bl memsetup
mov lr, ip
mov pc, lr
用我的话来说:启动方式有很多种,S3C2440,一般是从NORFLASH中开始启动,然后再把
前4K的内容转移到NANDFLASH。朱有鹏老师使用的210的开发板,比较高级一点,所以有很多方式,
需要识别那些SD卡,NANDFLASH,NORFLASH
Ldr ro, =PRO_ID_BASE
Ldr r1,[ro,#OMR_OFFSET]
Bic r2,r1,#0xffffffc1
3.1、启动介质由SOC的OMO:OM5的引脚的高低电平决定;
由#define PRO_ID_BASE 0xE0000000和#define OMR_OFFSET 0x04得到寄存器0xE000 0004
这个寄存器会根据OM引脚状况,硬件自动设置值。
(3)此三行代码后,r2存储了一个数字,后续通过该数字进行启动介质的判断。
//此处条件编译,删除部分代码
(4)通过判断r2中的值,来确定是从哪里启动的。
如果r2中的值为0xc,那从SD卡启动,然后把#BOOT_MMCSD(#define BOOT_MMCSD 0x3)赋给r3。
(5)最后两行中,因为有#define INF_REG_BASE 0xE010F000和#define INF_REG3_OFFSET 0x0c,则合成寄存器0xE010F00C,
然后把r3中的值放入其中。
5、start.s解析4
Lowlevel_init函数
(1)lowlevel_init在uboot\board\samsung\x210\lowlevel_init.S中。
(2)检查复位状态
因为复杂CPU支持多种复位状态(冷上电、休眠复位等),因此在复位代码中检查复位状态,判断到底是哪一种。
冷上电时DDR需要初始化,而休眠状态下复位不需要再次初始化DDR。
(3)IO状态恢复;
(4)关看门狗;
(5)一些与SRAM、SROM相关的GPIO设置(下图);
(6)开发板的供电锁存(下图)