开发板: XC2440
HOST : ubuntu 9.10
GCC : 4.3.2
现在市面上的学习板基本上是以u-boot来引导的,还有是用三星的vivi。不管是用什么来引导,他们的终级目标就是用来引导内核。
然而,有些很花哨的东西是我们根本不需要的,所以只是简单引导内核的话,主要是要完成时钟,串口,内存,NAND的初始化等,如果还想加其他功能的话,就根据需要的功能加一些函数进去。
首先来看下硬件初始化部分:
start.S 和 init.c
- #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:
- /* 1.关看门狗 */
- ldr r0, =0x53000000
- mov r1, #0
- str r1, [r0]
-
- /* 2.设置时钟 */
- ldr r0, =0x4c000014
- // mov r1, #0x03; // FCLK:HCLK:PCLK=1:2:4,HDIVN=1,PDIVN=1
- mov r1, #0x05; // FCLK:HCLK:PCLK=1:4:8,HDIVN=1,PDIVN=1
- str r1, [r0]
- /* 当FCLK与HCLK不相等时,要进行如下设置,更改CPU总线模式 */
- /* 如果HDIVN非0,CPU总线模式应该从"fast bus mode"变为 "asynchronous bus mode" */
- mrc p15, 0, r1, c1, c0, 0 /* 读出控制寄存器 */
- orr r1, r1, #0xc0000000 /* 设置为"asynchronous bus mode" */
- mcr p15, 0, r1, c1, c0, 0 /* 写入控制寄存器 */
- /* 设置MPLL */
- ldr r0, =0x4c000004
- // ldr r1, =S3c2440_MPLL_200MHZ
- ldr r1, =S3c2440_MPLL_400MHZ
- str r1, [r0]
-
- /* enable 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
- /* 3.初始化SDRAM */
- ldr r0, =MEM_CTL_BASE
- adr r1, sdram_config /* sdram_config的当前地址 */
- add r3, r0, #(13*4) /* 13个寄存器,每设置完一个地址加4 */
- 1:
- ldr r2, [r1], #4 /* 从r1所指的地方取一个值放到r2,然后让r1地址的值加4 */
- str r2, [r0], #4 /* 从r0所指的地方取一个值放到r2,然后让r0地址的值加4 */
- cmp r0, r3 /* 将r0与r3进行比较,判断SDRAM的13个寄存器有没有设置完 */
- bne 1b /* 返回前面的标号1,b是back */
-
- /* 4.重定位: 把bootloader本身的代码从flash复制到它的链接地址去 */
- ldr sp, =0x34000000 /* 设置栈,存放于内存的高地址 */
- bl nand_init
- mov r0, #0 /* 第一个参数为src,第二个参数为dest,第三个参数为len */
- ldr r1, =_start /* 目的地即为链接地址 在链接文件件定义 定义值为0xf33f80000 最高的512K 空间 */
- ldr r2, =__bss_start /* 没有初始化(或全初始化为0)的全局变量 */
- sub r2, r2, r1 /* 得到长度__bss_start的开始地址减去_start的地址即为要拷贝的长度 */
- bl copy_code_to_sdram
- bl clear_bss
-
- /* 5.执行main函数 */
- ldr lr, =halt /* 设置返回地址 */
- ldr pc, =main /**/
- halt:
- b halt
- 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
上面主要工作就是关看门狗,设置时钟,使能icache,重定位(如果程序非常小的话,重定位可以不需要),还有就是内存的初始化。nand的初始化用C写。内存的初始化就是往SDRAM的十三个控制寄存器中写值。
- /* NAND FLASH控制器 */
- #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 TXD0READY (1<<2)
- void nand_read(unsigned int addr, unsigned char *buf, unsigned int len);
- void putc(unsigned char c);
- void puts(char *str);
- /* 根据NorFlash的特点,0地址不能写来判断是否从NorFlash启动 */
- int isBootFromNorFlash(void)
- {
- volatile int *p = (volatile int *)0;
- int val;
- val = *p;
- *p = 0x12345678;
- if(*p == 0x12345678)
- {
- /* 写成功,nand启动 */
- *p = val;
- return 0;
- }
- else
- {
- /* Nor不能像内存一样写,写入失败,Nor启动 */
- return 1;
- }
-
- }
- void copy_code_to_sdram(unsigned char *src, unsigned char *dest, unsigned int len)
- {
- int i = 0;
- /* 如果是NOR启动 */
- if (isBootFromNorFlash())
- {
- while (i < len)
- {
- dest[i] = src[i]; /* 从源地址里读出一个值存给目的地址 */
- i++;
- }
- }
- else /* 否则从nand启动 */
- {
- // nand_init();
- nand_read((unsigned int)src, dest, len);
- }
-
- }
- void clear_bss(void)
- {
- extern int __bss_start, __bss_end; /* 定义为外部int变量 */
- int *p = &__bss_start; /* 取地址 */
- for (;p < &__bss_end; p++)
- *p = 0;
- }
- void nand_init(void)
- {
- #define TACLS 0
- #define TWRPH0 1
- #define TWRPH1 0
- /* 设置时序 */
- NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
- /* 使能NAND Flash控制器,初始化ECC, 禁止片选 */
- NFCONT = (1<<4)|(1<<1)|(1<<0);
- }
- void nand_select(void)
- {
- NFCONT &= ~(1<<1);
- }
- void nand_deselect(void)
- {
- NFCONT |= (1<<1);
- }
- void nand_cmd(unsigned char cmd)
- {
- volatile int i;
- NFCMMD = cmd;
- for (i = 0; i < 10; i++);
- }
- void nand_addr(unsigned int addr)
- {
- unsigned int col = addr % 2048;
- 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));
- }
- unsigned char nand_data(void)
- {
- return NFDATA;
- }
- void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
- {
- int col = addr % 2048;
- int i = 0;
-
- /* 1.选中 */
- nand_select();
- while (i < len)
- {
- /* 2.发出读命令00h */
- nand_cmd(0x00);
- /* 3.发出地址(分5个地址周期) */
- nand_addr(addr);
- /* 4.发出读命令30h */
- nand_cmd(0x30);
- /* 5.判断状态 */
- nand_wait_ready();
- /* 6.读数据 */
- for (; (col < 2048) && (i < len); col++)
- {
- buf[i] = nand_data();
- i++;
- addr++;
- }
- col = 0;
- }
- /* 7. 取消选中 */
- nand_deselect();
- }
- #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)
- /*
- * 初始化UART0
- * 115200,8N1, 无流控
- */
- void uart0_init(void)
- {
- GPHCON |= 0xa0; /* GPH2,GPH3用作TXD0,RXD0 */
- GPHUP = 0x0c; /* GPG2,GPH3内部上拉 */
- ULCON0 = 0x03; /* 8N1 */
- UCON0 = 0x05; /* 查询方式,UART时钟源为PCLK */
- UFCON0 = 0x00; /* 不使用FIFO */
- UMCON0 = 0x00; /* 不使用流控 */
- UBRDIV0 = UART_BRD; /* 波特率为115200 */
- }
- /*
- * 发送一个字节
- */
- void putc(unsigned char c)
- {
- /* 等待,直到发送缓冲区中的数据已经全部发送出去 */
- while (!(UTRSTAT0 & TXD0READY));
- /* 向UTXH0寄存器中写入数据,UART即自动将它发送出去 */
- UTXH0 = c;
- }
- void puts( char *str)
- {
- int i = 0;
- while (str[i])
- {
- putc(str[i]);
- i++;
- }
- }
- void puthex(unsigned int val)
- {
- /* 0x1234abcd */
- int i;
- int j;
- puts("0x");
- for (i = 0; i< 8; i++)
- {
- j = (val >> ((7-i)*4)) & 0xf;
- if((j>=0 ) && (j<=9))
- putc('0' + j);
- else
- putc('A' + j -0xa);
- }
- }
init.c中完成串口以及nand的初始化。nand的初始化要结合2440 nand控制器和nand datasheet来看。判断是从nor启动还是从nand启动主要是根据nor的0地址不可写来完成的。先向nor的0地址写入一个值,然后再将这个值读回来,如果读回来的值和写入的值不一样就说明是从nor启动,否则就是从nand启动。
bss: 没有初始化(或初始化为0的全局变量)
OOB:out of bank 这个是nandflash特有的,因为nandflash本身有缺陷,容易发生位反转。关于OOB区,是每个Page都有的。Page大小是512字节的NAND每页分配16字节的OOB;如果NAND物理上是2K的Page,则每个Page分配64字节的OOB。它是这样进行验证的,以保证写进去的数据和读出来的数据一致:写面数据,生成ECC,将ECC写入OOB;读页数据,算出ECC再和OOB里面的ECC进行比较。
void puthex
(unsigned
int val
);是用来打印16进制字符,是为了调试加进去的,可以读出内存中的值,和实际理论上的值进行比较判断。
再来看boot.c
- #include "setup.h"
- extern void uart0_init(void);
- extern void nand_read(unsigned int addr, unsigned char *buf, unsigned int len);
- extern void putc(unsigned char c);
- extern void puts(char *str);
- extern void puthex(unsigned int val);
- static struct tag *params;
- void setup_start_tag(void)
- {
- // params = (struct tag *) bd->bi_boot_params; /* bd->bi_boot_params值在移植好的源码中搜索直接用 */
- params = (struct tag *)0x30000100; /* 该值可以在移植好的u-boot里面找到。 */
- 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);
- }
- void setup_memory_tags(void)
- {
- params->hdr.tag = ATAG_MEM;
- params->hdr.size = tag_size (tag_mem32);
- // params->u.mem.start = bd->bi_dram[i].start;
- params->u.mem.start = 0x30000000;
- // params->u.mem.size = bd->bi_dram[i].size;
- params->u.mem.size = 0x4000000;
- params = tag_next(params);
- }
- int strlen(char *str)
- {
- int i = 0;
- while (str[i])
- {
- i++;
- }
- return i;
- }
- void strcpy(char *dest, char *src)
- {
- // char *tmp =dest;
- while ((*dest++ = *src++) != '\0');
- }
- void setup_commandline_tag(char *cmdline)
- {
- int len = strlen(cmdline) + 1;
- params->hdr.tag = ATAG_CMDLINE;
- // params->hdr.size =
- // (sizeof (struct tag_header) + strlen(p) + 1 + 4) >> 2;
- params->hdr.size = (sizeof (struct tag_header) + len + 3) >> 2; /* 向4取整 */
- strcpy (params->u.cmdline.cmdline, cmdline);
- params = tag_next(params);
- }
- void setup_end_tag(void)
- {
- params->hdr.tag = ATAG_NONE;
- params->hdr.size = 0;
- }
- int main(void)
- {
- void (*theKernel)(int zero, int arch, unsigned int params);
- volatile unsigned int *p = (volatile unsigned int *)0x30008000;
- /* 0.设置串口:内核启动时会从串口打印一些信息,但一开始内核没有初始化串口 */
- uart0_init();
- /* 1.从nand flash中把内核读入内存 */
- puts("Copy kernel from nand \n\r");
- nand_read(0x00120000 , (unsigned char *)0x30008000, 0x400000); /* 0x00120000这个地址要看之前的kernel分区,如果是烧uImage,还要加一个64字节的头 */
- /* 从0x00120000这个地址读到0x30008000,大小为0x400000 */
- puthex(0x1234ABCD);
- puts("\n\r");
- puthex(*p);
- puts("\n\r");
- /* 2.设置参数 */
- puts("Set boot params\n\r");
- setup_start_tag();
- setup_memory_tags();
- setup_commandline_tag("noinitrd root=/dev/mtdblock3 rootfstype=yaffs2 init=/linuxrc console=ttySAC0");
- setup_end_tag();
-
- /* 3.跳转执行 */
- puts("Boot kernel\n\r");
- theKernel = (void (*)(int, int, unsigned int ))0x30008000;
- theKernel(0, 362, 0x30000100);
- /*
- * mov pc,#0x30008000
- * ldr r1, =362
- * ldr r2, =0x30000100
- * mov pc, #0x30008000
- */
- puts("Error!\n\r");
- /* 如果一切正常,不会执行到这里 */
- return -1;
- }
这个函数大部分是参照u-boot中的相关函数写的。主要是几个tag的写法,看下tag的下定义
- struct tag {
- struct tag_header hdr;
- union {
- struct tag_core core;
- struct tag_mem32 mem;
- struct tag_videotext videotext;
- struct tag_ramdisk ramdisk;
- struct tag_initrd initrd;
- struct tag_serialnr serialnr;
- struct tag_revision revision;
- struct tag_videolfb videolfb;
- struct tag_cmdline cmdline;
- /*
- * Acorn specific
- */
- struct tag_acorn acorn;
- /*
- * DC21285 specific
- */
- struct tag_memclk memclk;
- } u;
- };
每一个tag里面有一个有tag头和一个联合。
bootloader编写步骤:
1.初始化硬件:关看门狗,设置时钟,设置SDRAM,设置串口,初始化nandflash。如果bootloader比较大,需要重定位到SDRAM。
2.把内核从nand flash读到SDRAM。
3.设置要传递给内核的参数。
4.跳转执行内核。
在这个过程中,很多参数是直接从XC2440里的启动信息中copy出来的。如nand从哪里读,读到哪里去,因为想直接用XC2440上面的内核与文件系统,偷点懒吧。
在这过程中,遇到一些问题,一是串口无输出,这种情况下检查下时钟和串口设置。读到nand时就不动了,这种可能是nand初始化没做好。一般内存按照手册设置好了就不会有问题。
这里有个奇怪的问题,设置传递参数的时候,如果按照小超板上的bootargs=noinitrd root=/dev/mtdblock2 init=/linuxrc console=ttySAC0 mem=64M来设的话,会出现:
Kernel panic - not syncing: No init found. Try passing init= option to kernel. See Linux Documentation/init.txt for guidance.
[
] (unwind_backtrace+0x0/0xec) from [] (panic+0x54/0x178)
[] (panic+0x54/0x178) from [] (init_post+0xa0/0xc4)
[] (init_post+0xa0/0xc4) from [] (kernel_init+0x10c/0x148)
[] (kernel_init+0x10c/0x148) from [] (kernel_thread_exit+0x0/0x8)
网上查了些资料,一般说文件系统没有做好,还有就是编译器的问题。但是用XC2440的u-boot没问题。没道理呀,都跑到这一步了,说明硬件初始化是没有问题,应该是传递参数有问题。
[u-boot@XC2440]# mtdparts
device nand0 , # parts = 4
#: name size offset mask_flags
0: bios 0x00100000 0x00000000 0
1: params 0x00020000 0x00100000 0
2: kernel 0x00400000 0x00120000 0
3: root 0x0fae0000 0x00520000 0
active partition: nand0,0 - (bios) 0x00100000 @ 0x00000000
defaults:
mtdids : nand0=nandflash0
mtdparts: mtdparts=nandflash0:1m@0(bios),128k(params),4m(kernel),-(root)
根据错误提示中的not syncing: No init found,我想这个文件应该是在文件系统中的,再看上面的分区信息。我抱着试下的心态将传入参数进行了修改:
bootargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0 mem=64M然后重启动板子,奇迹就出现了,可以正常进入系统。具体细节我还得问下XC,传递参数这里看会不会有其他问题。
至于想从SD什么的启动,在这基础上加吧,改吧,欢迎多交流!
u-boot.zip 极度瘦身版,编译出来2K左右。
uboot.zip 编译好的镜像,解压直接烧进去就行了。
使用方法,先用小超的u-boot烧好内核,文件系统,再烧入这个boot.bin就OK了。
阅读(298) | 评论(0) | 转发(0) |