uboot 第一阶段主要涉及文件:
start.S uboot.lds init,c makefiles
start.S
a.关看门狗 (让寄存器 0x53000000 置0)
b.设置时钟 (FCLK=200M,PCLK=50M)
cpu总线设置为异步模式
c.初始化SDRAM,把值写入SDRAM(可以c语言版,也可以汇编版式)
d.初始化nand flash 、(设置栈c语言)代码重定位 ==>init.c、uboot.lds
bl nand_init
--------------------------------------------------
//想用c语言来写的话先设置栈
mov r0,#0 /源:r0表示第一个参数,第一个参数是0/
ldr r1,=_start /目的地:连接地址,0x33f80000;第一个标号:_start/
ldr r2,=_bss_start /*伪汇编指令,两个,直接得到这个值*/
sub r2,r2,r1 /长度,要写Makefile和连接地址/
-----------------------------------------------------------
bl copy_code_to_sdram
bl clean_bss
void clean_bss(void)
{ //c语言里面引用链接脚本里的_bss_start, __bss_end :固定语法
extern int __bss_start, __bss_end; /定义为外部的变量/
int *p = &__bss_start; /取他们的地址/
for(;p<& __bss_end;p++)
{ *p=0; }
}
e.执行main函数 ==>main.c
ldr lr, =halt /设置main的返回地址/
ldr pc, =main /想让他跳到main函数执行/
/假设main能够返回,有返回值/
init.c
copy_code_to_sdram
if(isBootFromNorFlash()) //判断Nor启动还是nand启动
else
nand_read((unsigned int)addr,dest,len);/参数哪里来?从汇编里调入/
nand_select();
/b.发出读命令00h/
nand_cmd(0x00);
/c.发出地址(分5步发出)/
nand_addr(addr);
/d.发出读命令30h–确定要读/
nand_cmd(0x30);
/e.判断状态/
nand_wait_ready();
/f.读数据/
buf[i] =nand_data();
**
具体程序
**
Start.S
#define s3c2440_MPLL_200MHZ ((0x5c<<12)|(0x01<<4)|(0x02))
#define s3c2440_MPLL_400MHZ ((0x5c<<12)|(0x01<<4)|(0x01))
#define MEM_CTL_BASE 0x48000000
.text /代码段/
.global _start //全局标号
_start:
/a.关看门狗/
ldr r0,=0x53000000 //伪汇编指令
mov r1,#0
str r1,[r0] //r1->0
/*b.设置时钟,让CPU运行快点 */
/FCLK:HCLK:1:2:4,HDIVN=1,PDIVN=1/
ldr r0,=0x4c000014
mov r1,#0x03
str r1,[r0]
/手册要求,如果HDIVN非0,CPU总线模式应该从"fast bus mode"变为"asynchronous"/
mrc p15,0,r1,c1,c0,0 /读出控制寄存器/
orr r1,r1,#0xc0000000 /设置为asynchronous/
mcr p15,0,r1,c1,c0,0
/* MPLLCON=s3c2440_MPLL_200MHZ*/ //FCLK=200M,PCLK=50M
ldr r0,=0x4c000004
ldr r1,=s3c2440_MPLL_200MHZ/MHZ/
str r1,[r0]
/*启动ICACHE
mrc p15,0,r0,c1,c0,0 @ read control reg
orr r0,r0,#(1<<12)
mcr p15,0,r0,c1,c0,0 @ write it back
*/
/c.初始化SDRAM/
ldr r0,=MEM_CTL_BASE
adr r1,sdram_config /伪指令,得到标号的当前地址,非连接地址/
add r3,r0,#(4*13) /读出内存末地址/
1:
ldr r2,[r1],#4 /读出来之后地址+4 ,从r1读出一个值给r2 ,然后r1+4/
str r2,[r0],#4 /存r2的值/
cmp r0,R3
bne 1b /如果不相等,跳回去,1b:后面的1标号 1f:前面的1标号,可以有多个1标号/
/d.重定位(把bootload本身的代码从flash复制到它的连接地址)/
ldr sp,= 0x34000000 /想调用C语言前,要先设置栈/
/*设置栈,让栈指向一块内存就可以了,
*这个内存是64M,基地址是0x30000000,
让他指向最高内存,往下增长/
bl nand_init /*如果nor启动,就没有调用nandflash,
*但最后还是要从nandflash把内核,也要调用nand_init*/
/*nand_read的参数哪里来*/
mov r0,#0 /*源:r0表示第一个参数,第一个参数是0*/
ldr r1,=_start /*目的地:连接地址,0x33f80000;第一个标号:_start*/
ldr r2,=__bss_start /*伪汇编指令,两个_,直接得到这个值*/
sub r2,r2,r1 /*长度,要写Makefile和连接地址*/
//想用c语言来写的话
bl copy_code_to_sdram
bl clean_bss //bl相对跳转
/e.执行main/
//bl main /相对跳转,根据当前指令,找到main函数的偏差值,跳过去/
ldr lr, =halt /*设置main的返回地址*/
ldr pc, =main /*想让他跳到main函数执行*/
/*假设main能够返回,有返回值*/
halt:
b halt
//把这些值存放到SDRAM里面去
sdram_config:
.long 0x22011110 /BWSCON/
.long 0x00000700 /BANKCON0/
.long 0x00000700 /BANKCON1/
.long 0x00000700 /BANKCON2/
.long 0x00000700 /BANKCON3/
.long 0x00000700 /BANKCON4/
.long 0x00000700 /BANKCON5/
.long 0x00018005 /BANKCON6/
.long 0x00018005 /BANKCON7/
.long 0x008c04F4 /REFRESH/
.long 0x000000B1 /BANKSIZE/
.long 0x00000030 /MRSRB6/
.long 0x00000030 /MRSRB7/
初始化SDRAM(c语言版本,要先设置栈)
设置nand_flash时序
init.c
/oob是为了解决nand_flash的位反转问题
怎么访问00B
1)OOB里的第0字节
2)这一页里的第2048个
/nand FLASH 控制器/
#define NFCONF ((volatile unsigned long )0x4E000000) /(volatile unsigned char )0x4E000000为指针/
#define NFCONT (*(volatile unsigned char )0x4E000004) / (volatile unsigned char )0x4E000004为指针的值/
#define NFCMMD ((volatile unsigned char )0x4E000008)
#define NFADDR ((volatile unsigned char )0x4E00000C)
#define NFDATA ((volatile unsigned char )0x4E000010)
#define NFSTAT ((volatile unsigned char *)0x4E000020)
/GPIO配置串口,GPH2,GPH3用做TXD0,RXD0/
#define GPHCON (*(volatile unsigned long )0x56000070)
#define GPHUP ((volatile unsigned long *)0x56000078)
/UART0 registers/
#define ULCON0 (*(volatile unsigned long )0x50000000)
#define UCON0 ((volatile unsigned long )0x50000004)
#define UFCON0 ((volatile unsigned long )0x50000005)
#define UMCON0 ((volatile unsigned long )0x5000000C)
#define UTRSTAT0 ((volatile unsigned long )0x50000010)
#define UTXH0 ((volatile unsigned long )0x50000020)
#define URXH0 ((volatile unsigned long )0x50000024)
#define UBRDIV0 ((volatile unsigned long *)0x50000028)
#define UART_BRD 115200
void nand_init(void);
void uart0_init(void);
void putc(unsigned char c);
void puts(char *str);
void uart0_init(void)
{
GPHCON |= 0xa0; /GPH2,GPH3用做TXD0,RXD0/
GPHUP = 0x0c; /GPH2,GPH3内部上拉/
ULCON0 = 0x03; /*8n1(8个数据位,无校验,一个停止位)*/
UCON0 = 0x05; /*查询方式,UART时钟源为PCLK*/
UFCON0 = 0x00; /*不适应FIFO*/
UMCON0 = 0x00; /*不使用流控*/
UBRDIV0 = UART_BRD; /*波特率为115200*/
}
void nand_init(void)
{
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0
/设置时序 nandflash手册 芯片2440 对照/
NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
/使能NAND Flash控制器,初始化ECC,禁止片选/
NFCONT = (1<<4)|(1<<1)|(1<<0);
}
void putc(unsigned char c)
{
while(!(UTRSTAT0 & (1<<2)));
UTXH0=c;
}
void puts(char *str)
{
int i=0;
while(str[i])
{
putc(str[i]);
i++;
}
}
void nand_select()
{
NFCONT &=~(1<<1);
}
void disnand_select()
{
NFCONT |= (1<<1);
}
void nand_cmd(unsigned char cmd)
{
volatile int i;
NFCMMD =cmd;
for(i = 0;i < 10; i++); /发完命令后要加等待才算真正发了出去,立刻发可能会有问题/
}
void nand_addr(unsigned char addr)
{ /5个周期,前两个是列地址Column Address/
unsigned int col = addr %2048; /col这一页内哪一个地址(列地址),2048是11位/
unsigned int page = addr /2048; /**/
volatile int i;
NFADDR = col & 0xff;
for(i = 0;i < 10; i++);
NFADDR = (col >> 8) & 0xff;/表示从这一页的哪一列开始访问/
for(i = 0;i < 10; i++);
NFADDR = page & 0xff;/*要访问哪一页*/
for(i = 0;i < 10; i++);
NFADDR = (page >> 8) & 0xff;
for(i = 0;i < 10; i++);
NFADDR = (page >> 16) & 0xff;
for(i = 0;i < 10; i++);
}
void nand_wait_ready(void)
{
while(!(NFSTAT & 1)); /1表示接收完成/
}
unsigned char nand_data()
{
return NFDATA; /读这个寄存器就可以/
}
void nand_read(unsigned int addr,unsigned char *buf,unsigned int len)
{
int col = addr/2048; /有可能不是从0开始,而是从一页的某一列地址开始/
int i=0;
/a.选中/
nand_select();
while(i<len)
{
/b.发出读命令00h/
nand_cmd(0x00);
/c.发出地址(分5步发出)/
nand_addr(addr);
/d.发出读命令30h–确定要读/
nand_cmd(0x30);
/e.判断状态/
nand_wait_ready();
/f.读数据/
for(;(col<2048) && (i<len);col++)
{
buf[i] =nand_data();
i++;
addr++;
}
col=0;
}
}
int isBootFromNorFlash(void)
{
unsigned int val;
volatile int *p=(volatile int *)0; /*建立一个指针值向0地址*/
val = *p;
*p = 0x12345678;
if (*p==0x12345678)
{
*p =val;
return 0; /*boot from nand,nand启动==>对应的是内存*/
}
else
{
*p=val; /*NOR不能像内存一样读*/
return 1;
}
}
void copy_code_to_sdram(unsigned char *addr,unsigned char *dest,unsigned int len)
{
/从哪里拷贝:addr 目的地是dest,长度是len/
unsigned int i=0;
/*如果是NOR启动*/
if(isBootFromNorFlash())
{
while(i<len)
{
dest[i]=addr[i]; //*dest = *src
}
}
else
{
nand_read((unsigned int)addr,dest,len);/*参数哪里来?从汇编里调入*/
}
}
void clean_bss(void)
{ //c语言里面引用链接脚本里的_bss_start, __bss_end :固定语法
extern int __bss_start, __bss_end; /定义为外部的变量/
int *p = &__bss_start; /取他们的地址/
for(;p<& __bss_end;p++)
{ *p=0; }
}
boot.lds
SECTIONS {
. = 0x33f80000; //省麻烦,跟最高地址相差512K 直接放在512K
.text : { *(.text) }
. = ALIGN(4); //当前地址取整
.rodata : {*(.rodata)}
. = ALIGN(4);
.data : {*(.data)}
__bss_start = .; /*文件的大小等于这个地址-0x33f80000*/
.bss : { *(.bss) *(COMMON)}//bss段:未初始化的变量或初始化为0的变量
__bss_end = .; //编译出来的二进制文件里面不含有为0
}
Makefile
CC = arm-linux-gcc
LD = arm-linux-ld
AR = arm-linux-ar
OBJCOPY = arm-linux-objcopy
OBJDUMP = arm-linux-objdump
CFLAGS := -Wall -O2
CPPFLAGS := -nostdinc -fno-builtin
objs := start.o init.o boot.o
boot.bin:$(objs)
${LD} -Tboot.lds -o boot_elf $^
${OBJCOPY} -O binary -S boot_elf $@
${OBJDUMP} -D -m arm boot_elf > boot.dis
%.o:%.c
${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
%.o:%.S
${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
clean:
rm -f *.o *.bin *_elf *.dis