首先我们必须要知道,一开始我们的开发板上电的时候, 如果我们的板子是从norflash启动的,那么硬件会从nandflash拷贝其前4k的代码到内部RAM中, (这也是为什么我们的bootloader第一阶段需要在4k内),如果是norflash启动,那也是从norflash把前4k代码复制到内部RAM中
我们知道mini2440有个看门狗,如果说我们不能够定时去喂狗, 那么它会在超时的时候自动重启(硬件决定的),所以我们的bootloader的第一步就是
① 关闭看门狗
2440在启动的时候他是直接以外部晶振直接作为系统时钟,所以系统运行在12MHz的,这个频率实在是太小了, 所以我们为了让我们的板子运行速度加快,那么我们第二步就是
② 修改板子的时钟频率
在接下来我们想想要做什么呢?我们是不是想要启动内核,那启动内核之前是不是需要先把内核从nandflash(内核是放在nandflash的0x60000的)拷贝到内存SDRAM中,而这个SDRAM(64M)在进行读写前是需要你先去初始化他的一些存储单元才能够正常的进行读写的!!!!所以我们的第三部就是
③ 初始化SDRAM
我们把内核拷贝到SDRAM前,得先初始化nandflash, 才能够写nandflash,所以:
④ 初始化nandflash
接下来由于我们后面的操作是使用了c语言,故我们先要设置栈sp
⑤ 设置栈sp
那么我们接下来是不是就是去把内核拷贝到SDRAM中呢?别急,我们要知道我们的bootloader目前只有前4k的代码被拷贝到内部RAM中,如果说我们的bootloader超过4k的话那么我们的bootloader就很有可能会出问题, 所以我们接下来要做的就是拷贝bootloader到SDRAM中,再跳过去运行, 这叫做:
⑥ 重定位
重定位结束后,我们需要先清除bss段,也就是赋值为0, 具体什么是bss段这里就不解释了
⑦ 清bss段
接下来进入bootloader第二阶段
接下来我们终于到了把内核从nandflash中拷贝到SDRAM中,这就涉及到读nandflash了,而nandflash读的时候呢,需要我们先去
⑧ 为了调试方便我们在这里初始化串口, 让串口能够为我们打印一下提示信息
⑨ 把内核从nandflash拷贝到SDRAM中
⑩ 设置好传递给内核的参数 调用内核
接下来讲解具体的代码, 首先是 start.s 这个是第一阶段的文件.
- .text
- .global _start
- _start:
-
- ldr r0 ,= 0x53000000
- mov r1 , #0
- str r1 , [r0]
-
- ldr r0 ,= 0x4c000014
- mov r1 , #0x5
- str r1 , [r0]
-
-
-
- mrc p15, 0, r1, c1, c0, 0
- orr r1, r1, #0xc0000000
- mcr p15, 0, r1, c1, c0, 0
-
-
- ldr r0 ,= 0x4c000004
- ldr r1 ,= ((0x7f<<12)|(0x2<<4)|0x1)
- str r1,[r0]
-
-
- 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
-
-
-
-
- ldr r0 ,= 0x48000000
- adr r1 ,sdram_config
- add r3 , r0 ,#(13*4)
- 0:
- ldr r2 , [r1] ,#4
- str r2 , [r0] ,#4
- cmp r0,r3
- bne 0b
-
-
- ldr r0 ,= 0x56000010
- ldr r1 ,= (0x55<<10)
- str r1,[r0]
-
- ldr sp,=0x34000000
- bl uart0_init
-
- bl nand_init
-
- mov r0 , #0
- ldr r1 ,= 0x33f80000 @_start @0x33f80000
- ldr r2 ,= __bss_start
- @sub r2 , r2 , r1
-
- bl copy_code_to_sdram
-
- bl clear_bss
-
-
- ldr lr ,= halt
- ldr pc ,= Main
- bl led_on
-
- halt:
- b halt
-
-
- sdram_config:
- .long 0x22011110
- .long 0x00000700
- .long 0x00000700
- .long 0x00000700
- .long 0x00000700
- .long 0x00000700
- .long 0x00000700
- .long 0x00018005
- .long 0x00018005
- .long 0x008C04F4
- .long 0x000000B1
- .long 0x00000030
- .long 0x00000030
首先看我们的uart_init()初始化串口:
- void uart0_init(void)
- {
- GPHCON = (GPHCON&(~(0xf<<4)))|(0xa<<4);
- UFCON0 = 0x00;
- UMCON0 = 0x00;
- ULCON0 = 0x03;
- UCON0 = 0x05;
- UBRDIV0=((int)(50000000/(UART_BAUD_RATE*16))-1);
-
- }
串口设置好后, 很明显我们需要写串口输出的函数:
-
- void _putc(char c)
- {
-
- while (!(UTRSTAT0 & TXD0READY));
-
-
- UTXH0 = c;
- }
-
- void _puts(char *str)
- {
- while(*str){
- _putc(*str++);
- }
- }
先贴下宏定义:
- #define uchar unsigned char
- #define uint unsigned int
-
- #define NFCONF (*((volatile unsigned long *)0x4E000000))
- #define NFCONT (*((volatile unsigned long *)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))
-
-
- #define GPHCON (*(volatile unsigned long *)0x56000070)
- #define GPHUP (*(volatile unsigned long *)0x56000078)
-
-
- #define ULCON0 (*(volatile unsigned long *)0x50000000)
- #define UCON0 (*(volatile unsigned long *)0x50000004)
- #define UFCON0 (*(volatile unsigned long *)0x50000008)
- #define UMCON0 (*(volatile unsigned long *)0x5000000c)
- #define UTRSTAT0 (*(volatile unsigned long *)0x50000010)
- #define UTXH0 (*(volatile unsigned char *)0x50000020)
- #define URXH0 (*(volatile unsigned char *)0x50000024)
- #define UBRDIV0 (*(volatile unsigned long *)0x50000028)
-
- #define PCLK 50000000 // init.c中的clock_init函数设置PCLK为50MHz
- #define UART_CLK PCLK // UART0的时钟源设为PCLK
- #define UART_BAUD_RATE 115200 // 波特率
- #define UART_BRD ((UART_CLK / (UART_BAUD_RATE * 16)) - 1)
-
- #define u32 unsigned long
-
- #define TXD0READY (1<<2)
接下来我们看看nandflash的初始化函数nand_init()
- void nand_init(){
- #define TACLS 0
- #define TWRPH0 1 // hclk * (twrph0+1) = 0.1ns * (twrph0 + 1)>=12
- #define TWRPH1 0
-
- NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4 );
-
- NFCONT = (1<<4)|(1<<1)|(1<<0);
- }
接下来到了copy_code_to_sdram(), 也就是重定位
- void copy_code_to_sdram(uchar* src_addr,uchar* dest,uint len){
-
- uint i = 0;
- _puts("copy_code_to_sdram\n\r");
- if(isBootFromNorFlash()){
- _puts("NorFlash\n\r");
- while(i
- *dest++ = *src_addr++;
- i++;
- }
- }else{
- _puts("Nand Flash\n\r");
- nand_read((uint)src_addr,dest,len);
- }
- _puts("copy_code_to_sdram end .................\n\r");
- }
nandflash的读操作是要参考数据手册的,
- 片选
- 发送00h命令
- 发送地址5周期
- 发送读命令 30h
- 判断状态
- 读..
- 取消片选
- void nand_read(uint src_addr,uchar* dest,uint len){
- uint i = 0 , j=0;
- int col = src_addr%2048;
-
- nand_select();
-
- while(i < len){
-
- nand_cmd(0x0);
-
- nand_addr(src_addr);
-
- nand_cmd(0x30);
-
- nand_wait_ready();
-
-
- for(;(col<2048)&&(i
- dest[i++] = nand_data();
- }
- if(!((j++)%20))
- _putc('#');
- col = 0;
-
- }
-
- nand_disselect();
- _puts("\n\r");
- _puts("nand_read end\n\r");
- }
- void nand_cmd(uchar cmd){
- volatile int i = 10;
- NFCMMD = cmd;
- while(i--);
- }
-
- void nand_addr(uint addr){
- uint col = addr % 2048;
- uint page = addr / 2048;
- volatile int i;
- NFADDR = col&0xff;
- for(i = 10 ;i; i--);
- NFADDR = (col>>8)&0xff;
- for(i = 10 ;i; i--);
- NFADDR = page&0xff;
- for(i = 10 ;i; i--);
- NFADDR = (page>>8)&0xff;
- for(i = 10 ;i; i--);
- NFADDR = (page>>16)&0xff;
- for(i = 10 ;i; i--);
- }
-
- void nand_wait_ready(){
- while(!(NFSTAT&1));
- }
-
- uchar nand_data(){
- return NFDATA;
- }
-
- void nand_select(){
- NFCONT &= ~(1<<1);
- }
- void nand_disselect(){
- NFCONT |= (1<<1);
- }
接下来是清bss段
- void clear_bss()
- {
- extern int __bss_start , __bss_end;
- int *p = &__bss_start;
- _puts("clear_bss\n\r");
- while(p<&__bss_end){
- *p++ = 0;
- }
- }
好啦, 我们跳到Main函数, 也就是我们的第二阶段啦:
- void Main(){
- void (*theKernel)(int zero, int arch, uint params);
-
-
- nand_read(0x60000, (uchar *)0x30008000, 0x500000);
-
- _puts("set params \n\r");
- setup_start_tag ();
- setup_memory_tags ();
- setup_commandline_tag ("noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0");
- setup_end_tag ();
-
-
-
- _puts("boot kernel \n\r");
- theKernel = (void (*)(int ,int ,uint))(0x30008000);
- theKernel (0, 1999, 0x30000100);
-
- _puts("error............. \n\r");
-
- }
- static struct tag *params;
-
- static void setup_start_tag ()
- {
- params = (struct tag *)0x30000100;
-
- params->hdr.tag = ATAG_CORE;
- params->hdr.size = tag_size (tag_core);
- params->u.core.flags = 0;
- params->u.core.pagesize = 0;
- params->u.core.rootdev = 0;
-
- params = tag_next (params);
- }
- static void setup_memory_tags ()
- {
-
- params->hdr.tag = ATAG_MEM;
- params->hdr.size = tag_size (tag_mem32);
-
- params->u.mem.start = 0x30000000;
- params->u.mem.size = 64*1024*1024;
-
- params = tag_next (params);
- }
-
- static uint _strlen(char *buf){
- uint i=0;
- while(*buf++){
- i++;
- }
- return i;
- }
-
- static void my_strcpy(char * src ,char *dest){
-
- while ((*dest++ = *src++) != '\0');
- }
- static void setup_commandline_tag ( char *commandline)
- {
-
- int len = _strlen(commandline)+1;
- _puts("setup_commandline_tag now \n\r");
- params->hdr.tag = ATAG_CMDLINE;
- params->hdr.size = (sizeof (struct tag_header) + len + 3) >> 2;
-
- my_strcpy(params->u.cmdline.cmdline, commandline);
- params = tag_next (params);
- }
-
- static void setup_end_tag ()
- {
- params->hdr.tag = ATAG_NONE;
- params->hdr.size = 0;
- }
最后附上链接脚本:
- SECTIONS {
- . = 0x0;
- .text : { *(.text) }
-
- . = ALIGN(4);
- .rodata : {*(.rodata)}
-
- . = ALIGN(4);
- .data : { *(.data) }
-
- . = ALIGN(4);
- __bss_start = .;
- .bss : { *(.bss) *(COMMON) }
- __bss_end = .;
- }
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
-
-
- objs := start.o init.o Main.o
- boot.bin:$(objs)
- $(LD) -o boot.elf -Tboot.lds $^ # 链接
- $(OBJCOPY) -O binary -S boot.elf $@ # 转为2进制
- $(OBJDUMP) -D -m arm boot.elf > boot.dis # 反汇编
-
- %.o:%.c
- $(CC) -o $@ -c $< $(CPPFLAGS) $(CFLAGS)
-
- %.o:%.s
- $(CC) -o $@ -c $< $(CPPFLAGS) $(CFLAGS)
-
- clean:
- rm -r *.o *.elf *.bin *.dis
代码: