AT91SAM9260
ARM926EJ-S
u-boot
一.配置和编译链接
配置:
Make (板级)XXX_config
Make
具体的Makefile中,
在配置项中还可以加入其他配置宏,用来增加新的配置宏定义或覆盖include/configs/<板名称>.h下的同名宏定义。比如:
echo "#define PHYS_SDRAM_SIZE 0x2000000" >> $(obj)include/config.h
(添加配置宏到config.h中)
在每个板级代码目录下,都有一个u-boot.lds文件,是一个GCC的链接脚本,表明了U-BOOT镜像的组成以及一些段位置的定义。
在AT91SAM9260上,设计了低级初始化部分,以支持从NAND启动。所有低级初始化用到的代码都必须位于前4KB位置。所以,文件u-boot.lds中,把这些代码放在镜像的前面,如下所示:(类似scatter文件)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
cpu/arm926ejs/start.o (.text)
cpu/arm926ejs/at91sam926x/nboot.o (.text)
cpu/arm926ejs/at91sam926x/nboot.o (.rodata)
cpu/arm926ejs/at91sam926x/ebi.o (.init.lowlevel)
*(.text)
}
...
在ebi.o中,不是所有代码都被低级初始化使用的,所以将低级初始化相关代码定义在.init.lowlevel段中。对应函数声明如下:
int __attribute__((__section__(".init.lowlevel"))) AT91F_InitSDRAM (u32 cr, u32 lpr)
镜像的运行期起始地址在config.mk中定义,如下所示:
sinclude $(OBJTREE)/board/$(BOARDDIR)/config.tmp
ifndef TEXT_BASE
TEXT_BASE = xxx(这个根据板级设置进行配置)
Endif
宏TEXT_BASE就表明了运行期起始地址。这里还包含了config.tmp文件,是为了可以在U-BOOT根目录下的Makfile文件中定义不同的起始地址
二.Start.s
启动UBOOT的一些要求,
禁止中断,关闭MMU,SVC32模式,打开I-CACHE(指令缓存),关闭数据CACHE
对于CPU/ARM926EJS/start.s(同样,对于一些#define也依赖于XXX.h(板级头文件))
定义 _TEXT_BASE, _bss_start,_bss_end,_armboot_start,
初始化之后,b reset,Reset中,设置为SVC32 mode之后,
Bl cpu_init_crit(一般情况下不应该skip lowlevel init)
Cpu_init_crit打开I-CACHE,关闭D-CACHE,关闭MMU,然后ip寄存器保存lr的值,即lowlevel_init的返回地址,bl lowlevel_init(函数在lowlevel_init.s中)
Cpu_init_crit返回后继续运行,跳转到C语言的接口start_armboot
(这在stage2中说明)
三.Lowlevel_init.s
lowlevel_init中首先设置栈寄存器SP,
然后
Mov r5,lr
Mov r6,ip
/*
*将寄存器lr和ip分别保存r5和r6中。
*这两个寄存器分别存放函数cpu_init_crit调用本函数AT91F_LowLevelInit的返回地址
*以及ARM复位向量调用函数lowlevel_init的返回地址
*/
/*
*1.当使用bl或者blx跳转到子过程的时候,(lr)r14保存了返回地址,可以在调用过程结尾恢复。
*2.异常中断发生时,这个异常模式特定的物理R14被设置成该异常模式将要返回的地址。
*/
bl AT91F_LowLevelInit(完成硬件初始化该函数在board_lowlevel_init.c中)
接着如果定义了从nand boot则第一次拷贝
Bl copy_start,
由于nand boot需要硬件将nand flash的前4K拷贝到内部的SDRAM0,其中vaild image detect要求偏移的第六向量,即0x14必须为要拷贝的长度,这里设置的是4K大小,查看u-boot.bin也可以看到0x14对应的数据位00100000
(具体的可以看AT91SAM9260的SPEC中关于valid image detect部分)
这里将前4k的数据拷贝到_text_base起始的SDRAM地址,然后将程序的PC设置成加上_text_base的lr的地址,在SDRAM上继续运行(而没有重新开始运行)
Bl copy_main(继续讲剩下的代码拷贝到SDRAM中,该函数在nboot.c中。)
四.Board_lowlevel_init.c
a) AT91F_LowLevelInit完成硬件相关初始化
该函数执行过程如下:
1.如果有使用以太网(定义了配置宏CONFIG_DRIVER_ETHER和CONFIG_CMD_NET),则配置以太网PHY接口的口线
2.如果允许了用户复位功能(定义了配置宏CONFIG_USR_RESET),则开启用户复位功能
3.禁止看门狗
4.调用函数AT91F_SetPLL设置锁相环PLLA,并配置CPU时钟和总线时钟
5.调用函数AT91F_AIC_DisableAll禁止所有AIC中断。函数AT91F_AIC_DisableAll设置寄存器AIC_IDCR禁止所有AIC中断,并设置寄存器AIC_ICCR清空所有未处理中断
6.连续调用8次AT91F_AIC_AcknowledgeIt(advance interrupt control)响应AIC中断:9G20/9260最多可以存储8个未处理的不同优先级中断,所以调用8次以全部清除。函数AT91F_AIC_AcknowledgeIt设置寄存器AIC_EOICR以表明中断结束
7.允许CPU系统控制区部分时钟
8.配置SDRAM:依次按64MB、32MB、16MB三种容量调用函数AT91F_InitSDRAM进行配置并测试读写,直到找到匹配的配置。传递到函数AT91F_InitSDRAM的参数cfg和lpr定义如下:
(1)SDRAM配置寄存器值cfg:包括:
a. 4个BANK、16位总线
b. 时序TWR=2、TRC=9,TRP=3, TRCD=3, TRAS=6,TXSR=10;如果是9G20,CAS为3;否则为2
c. 根据SDRAM容量设置行数和列数
(2)SDRAM低功耗控制寄存器值lpr:宏BOARD_SDRAM_LPR,即允许在SDRAM空闲64个周期后自动进入自我刷新模式
9.调用函数AT91F_InitSMC配置NAND FLASH所在片选时序
b) AT91F_SetPLL
该函数执行过程如下:
1、禁止电源管理控制器PMC的所有中断
2、检查主时钟是否已经起振(CKGR_MCFR寄存器的MAINRDY位),如果没有,则进行如下过程以启动主时钟
(1)设置寄存器CKGR_MOR:域OSCOUNT为最大,位MOSCEN为1,以启动主时钟
(2)等待主时钟稳定:反复检查寄存器PMC_SR,直到位MOSCS为1
3、配置锁相环PLLA并启动:即设置寄存器CKGR_PLLAR来配置锁相环PLLA,并反复检查寄存器PMC_SR,直到位LOCKA为1。配置过程如下:
(1)PLLA稳定时间(域PLLACOUNT):为35或42个慢时钟周期。9260/9G20的PLLA启动时间根据ATMEL提供的计算公式得出。慢时钟可能是外部32.768KHZ或内部RC(20KHZ以上,不定),这决定了不同的PLLA稳定时间的周期数。慢时钟源根据寄存器PMC_SR的OSC_SEL位决定
(2)乘法因子(MULA)和除法因子(DIVA):使用函数cpu_is_at91sam9g20判断处理器是9260还是9G20,如果是9260,则输出191.6928MHZ;9G20则要输出796.2624MHZ。这还影响到域OUTA的取值,因为PLLA输出时钟不同频率下,必需设置不同的OUTA的值,这根据Datasheet中要求决定
4、配置CPU时钟和总线时钟:按照Datasheet要求的过程进行设置,如下:
检查寄存器PMC_MCKR的CSS域,如果输入时钟源不是PLLA输出时钟,则单独修改输入时钟源为PLLA输出时钟,并反复检查寄存器PMC_SR,直到位MCKRDY为1
如果处理器为9G20,则设置CPU时钟为PLLA输出时钟的1/2,总线时钟为PLLA输出时钟的1/6,即分别为398.1312MHZ和132.7104MHZ。如果是9260,则CPU时钟就是PLLA输出时钟,而总线时钟则为PLLA输出时钟的1/2,分别为191.6928MHZ和95.8464MHZ。
反复检查寄存器PMC_SR,直到位MCKRDY为1
c) __inline AT91F_InitSMC(void)
该函数分别配置寄存器SMC_SETUP3、SMC_PULSE3、SMC_CYCLE3和SMC_CTRL3,即片选3的配置,包括:
(1)总线宽度:位于SMC_CTRL3,总线宽度根据宏CFG_NAND_WIDTH决定,如果包含位NAND_BUSWIDTH_16,则需要定义16位数据线;否则定义8位数据线
(2)片选类型:目前是支持读写、不使用NWAIT信号
(3)时序:9G20和9260主频不同,所以这些寄存器中存储的各种时序周期数也不同
五.Copy_main(nboot.c)
这里涉及到nand flash的操作,不过没有使用MTD框架
(1) at91_nand_init
初始化主要的是口线的设置,相关寄存器的配置,包括R/B,CS
这里CLE和ALE与地址线相连所以操作地址时就操作了相应pin
(2) nboot_get_flash_type
通过调用nboot_get_flash_type获取NAND FLASH类型,包括页大小,块大小,OOB区域大小。
1. 片选nand flash
2. 发送命令90,地址为0, 读取前两字节的ID,(具体可参照u-boot之NAND FLASH)
如果第二个字节小于0XA1,则页大小不超过512字节,指定页大小为512字节,块大小为16K,OOB为16字节
否则,再读取两字节数据,ID号第4个字节可以获取页大小、块大小、OOB区域大小,并可知道是8位还是16位 NAND FLASH,如下:
(a)第0~1位内容确定页大小:1024* 2^x。
(b)第2位确定OOB区域大小:(页大小/512) * (8 * 2^x)
(c)第4~5位确定块大小:64KB* 2^x
(d)第6位:1-16位NAND FLASH,0-8位NAND FLASH
3. nboot_ecc_init
通过页大小配置ECC寄存器,包括MR,CR
4. 通过页大小配置ECC校验字节位置和坏块标志字节,对于512字节的页,ECC校验字节位置为前4字节,坏块标志字节为5,6字节
5. 关闭片选
(3) 找到起始页,发送命令0,行地址为0,列地址为当前页偏移,表明需要读取一页的数据,接着读取一页的数据(此数据保存于SDARM中,是BOOT的后续代码),检查是否坏块,检查数据是否有错误,如果只是1 bit的错误,则可以纠正,
否则,读取下一页的数据,重复以上工作,知道endp(_bss_start)
在读取下一页的数据之前,需反复调用函数nboot_devready,直到NAND FLASH就绪,这是因为部分NAND FLASH有自动递增功能,即读完一页内容后会自动把下页内容送入NAND FLASH缓冲区,在此期间NAND FLASH处于忙状态,不能响应指令。
六.u-boot.lds
该文件为编译的链接文件,类似于scatter文件
OUTPUT_FORMAT,OUTPUT_ARCH说明的使用的小端模式,体系为ARM体系
ENTRY(_start)说明程序的入口地址是_start
Nand boot需要将前4K的代码拷贝到内部SDRAM0中,所以在分配段时,尽量把stage1初始化的代码分配到前4K中。
.text :
{
cpu/arm926ejs/start.o (.text)
cpu/arm926ejs/at91sam926x/nboot.o (.text)
cpu/arm926ejs/at91sam926x/nboot.o (.rodata)
cpu/arm926ejs/at91sam926x/ebi.o (.init.lowlevel)
*(.text)
}
将汇编相关的放在最前面,这里将ebi.o中段名为.init.lowlevel中的内容放于.text段中,而这真是前面提到的AT91F_InitSDRAM,接着分配的就是rodata 和data,
而
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
这里将所有的u_boot_cmd命令分配到以_u_boot_cmd_start开始的地址上,便于后面通过命令名查找cmd_trl_t结构,然后调用相应的方法。
最后就是_bss段
__bss_start = .;
.bss : { *(.bss) }
_end = .;
以上就是U-BOOT中的stage1部分,在下篇中会介绍stage2