Chinaunix首页 | 论坛 | 博客
  • 博客访问: 619595
  • 博文数量: 75
  • 博客积分: 988
  • 博客等级: 准尉
  • 技术积分: 1269
  • 用 户 组: 普通用户
  • 注册时间: 2011-05-10 15:44
文章分类

全部博文(75)

文章存档

2022年(1)

2019年(1)

2018年(1)

2016年(9)

2015年(7)

2013年(6)

2012年(40)

2011年(10)

分类: LINUX

2012-07-11 15:56:40

开发板: XC2440
HOST  :  ubuntu 9.10
GCC   :  4.3.2

现在市面上的学习板基本上是以u-boot来引导的,还有是用三星的vivi。不管是用什么来引导,他们的终级目标就是用来引导内核。

然而,有些很花哨的东西是我们根本不需要的,所以只是简单引导内核的话,主要是要完成时钟,串口,内存,NAND的初始化等,如果还想加其他功能的话,就根据需要的功能加一些函数进去。

首先来看下硬件初始化部分:
start.S 和 init.c

点击(此处)折叠或打开

  1. #define S3c2440_MPLL_200MHZ ((0x5c<<12)|(0x01<<4)|(0x02))
  2. #define S3c2440_MPLL_400MHZ ((0x5c<<12)|(0x01<<4)|(0x01))
  3. #define MEM_CTL_BASE 0x48000000

  4. .text
  5. .global _start
  6. _start:

  7. /* 1.关看门狗 */
  8. ldr r0, =0x53000000
  9. mov r1, #0
  10. str r1, [r0]

  11. /* 2.设置时钟 */
  12. ldr r0, =0x4c000014
  13. // mov r1, #0x03; // FCLK:HCLK:PCLK=1:2:4,HDIVN=1,PDIVN=1
  14. mov r1, #0x05; // FCLK:HCLK:PCLK=1:4:8,HDIVN=1,PDIVN=1
  15. str r1, [r0]



  16. /* 当FCLK与HCLK不相等时,要进行如下设置,更改CPU总线模式 */
  17. /* 如果HDIVN非0,CPU总线模式应该从"fast bus mode"变为 "asynchronous bus mode" */
  18. mrc p15, 0, r1, c1, c0, 0 /* 读出控制寄存器 */
  19. orr r1, r1, #0xc0000000 /* 设置为"asynchronous bus mode" */
  20. mcr p15, 0, r1, c1, c0, 0 /* 写入控制寄存器 */

  21. /* 设置MPLL */
  22. ldr r0, =0x4c000004
  23. // ldr r1, =S3c2440_MPLL_200MHZ
  24. ldr r1, =S3c2440_MPLL_400MHZ
  25. str r1, [r0]

  26. /* enable icache */
  27. mrc p15, 0, r0, c1, c0, 0 @ read control reg
  28. orr r0, r0, #(1<<12)
  29. mcr p15, 0, r0, c1, c0, 0 @ write it back
  30. /* 3.初始化SDRAM */
  31. ldr r0, =MEM_CTL_BASE
  32. adr r1, sdram_config /* sdram_config的当前地址 */
  33. add r3, r0, #(13*4) /* 13个寄存器,每设置完一个地址加4 */

  34. 1:
  35. ldr r2, [r1], #4 /* 从r1所指的地方取一个值放到r2,然后让r1地址的值加4 */
  36. str r2, [r0], #4 /* 从r0所指的地方取一个值放到r2,然后让r0地址的值加4 */
  37. cmp r0, r3 /* 将r0与r3进行比较,判断SDRAM的13个寄存器有没有设置完 */
  38. bne 1b /* 返回前面的标号1,b是back */

  39. /* 4.重定位: 把bootloader本身的代码从flash复制到它的链接地址去 */
  40. ldr sp, =0x34000000 /* 设置栈,存放于内存的高地址 */

  41. bl nand_init

  42. mov r0, #0 /* 第一个参数为src,第二个参数为dest,第三个参数为len */
  43. ldr r1, =_start /* 目的地即为链接地址 在链接文件件定义 定义值为0xf33f80000 最高的512K 空间 */
  44. ldr r2, =__bss_start /* 没有初始化(或全初始化为0)的全局变量 */
  45. sub r2, r2, r1 /* 得到长度__bss_start的开始地址减去_start的地址即为要拷贝的长度 */
  46. bl copy_code_to_sdram
  47. bl clear_bss

  48. /* 5.执行main函数 */
  49. ldr lr, =halt /* 设置返回地址 */
  50. ldr pc, =main /**/

  51. halt:
  52. b halt

  53. sdram_config:
  54. .long 0x22011110 //BWSCON
  55. .long 0x00000700 //BANKCON0
  56. .long 0x00000700 //BANKCON1
  57. .long 0x00000700 //BANKCON2
  58. .long 0x00000700 //BANKCON3
  59. .long 0x00000700 //BANKCON4
  60. .long 0x00000700 //BANKCON5
  61. .long 0x00018005 //BANKCON6
  62. .long 0x00018005 //BANKCON7
  63. .long 0x008C04F4 //REFRESH
  64. .long 0x000000B1 //BANKSIZE
  65. .long 0x00000030 // MRSRB6
  66. .long 0x00000030 // MRSRB7
上面主要工作就是关看门狗,设置时钟,使能icache,重定位(如果程序非常小的话,重定位可以不需要),还有就是内存的初始化。nand的初始化用C写。内存的初始化就是往SDRAM的十三个控制寄存器中写值。


点击(此处)折叠或打开

  1. /* NAND FLASH控制器 */
  2. #define NFCONF (*((volatile unsigned long *)0x4E000000))
  3. #define NFCONT (*((volatile unsigned long *)0x4E000004))
  4. #define NFCMMD (*((volatile unsigned char *)0x4E000008))
  5. #define NFADDR (*((volatile unsigned char *)0x4E00000C))
  6. #define NFDATA (*((volatile unsigned char *)0x4E000010))
  7. #define NFSTAT (*((volatile unsigned char *)0x4E000020))

  8. #define GPHCON (*(volatile unsigned long *)0x56000070)
  9. #define GPHUP (*(volatile unsigned long *)0x56000078)

  10. /* 串口寄存器 */
  11. #define ULCON0 (*(volatile unsigned long *)0x50000000)
  12. #define UCON0 (*(volatile unsigned long *)0x50000004)
  13. #define UFCON0 (*(volatile unsigned long *)0x50000008)
  14. #define UMCON0 (*(volatile unsigned long *)0x5000000c)
  15. #define UTRSTAT0 (*(volatile unsigned long *)0x50000010)
  16. #define UTXH0 (*(volatile unsigned char *)0x50000020)
  17. #define URXH0 (*(volatile unsigned char *)0x50000024)
  18. #define UBRDIV0 (*(volatile unsigned long *)0x50000028)

  19. #define TXD0READY (1<<2)

  20. void nand_read(unsigned int addr, unsigned char *buf, unsigned int len);
  21. void putc(unsigned char c);
  22. void puts(char *str);




  23. /* 根据NorFlash的特点,0地址不能写来判断是否从NorFlash启动 */
  24. int isBootFromNorFlash(void)
  25. {
  26.     volatile int *p = (volatile int *)0;

  27.     int val;

  28.     val = *p;
  29.     *p = 0x12345678;

  30.     if(*p == 0x12345678)
  31.         {
  32.             /* 写成功,nand启动 */
  33.             *p = val;
  34.             return 0;
  35.         }
  36.     else
  37.         {
  38.             /* Nor不能像内存一样写,写入失败,Nor启动 */
  39.             return 1;
  40.         }
  41.     
  42. }

  43. void copy_code_to_sdram(unsigned char *src, unsigned char *dest, unsigned int len)
  44. {
  45.     int i = 0;
  46.     /* 如果是NOR启动 */
  47.     if (isBootFromNorFlash())
  48.         {
  49.             while (i < len)
  50.                 {
  51.                     dest[i] = src[i]; /* 从源地址里读出一个值存给目的地址 */
  52.                     i++;
  53.                 }
  54.         }
  55.     else /* 否则从nand启动 */
  56.         {
  57.            // nand_init();
  58.             nand_read((unsigned int)src, dest, len);
  59.         }
  60.     
  61. }

  62. void clear_bss(void)
  63. {
  64.     extern int __bss_start, __bss_end; /* 定义为外部int变量 */
  65.     int *p = &__bss_start; /* 取地址 */

  66.     for (;p < &__bss_end; p++)
  67.         *p = 0;
  68. }

  69. void nand_init(void)
  70. {
  71. #define TACLS 0
  72. #define TWRPH0 1
  73. #define TWRPH1 0

  74.     /* 设置时序 */
  75.     NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
  76.     /* 使能NAND Flash控制器,初始化ECC, 禁止片选 */
  77.     NFCONT = (1<<4)|(1<<1)|(1<<0);
  78. }

  79. void nand_select(void)
  80. {
  81.     NFCONT &= ~(1<<1);
  82. }

  83. void nand_deselect(void)
  84. {
  85.     NFCONT |= (1<<1);
  86. }

  87. void nand_cmd(unsigned char cmd)
  88. {
  89.     volatile int i;
  90.     NFCMMD = cmd;
  91.     for (i = 0; i < 10; i++);
  92. }

  93. void nand_addr(unsigned int addr)
  94. {
  95.     unsigned int col = addr % 2048;
  96.     unsigned int page = addr / 2048;

  97.     volatile int i;

  98.     NFADDR = col & 0xff;
  99.     for (i = 0; i < 10; i++);
  100.     NFADDR = (col >> 8) & 0xff;
  101.     for (i = 0; i < 10; i++);
  102.     
  103.     NFADDR = page & 0xff;
  104.     for (i = 0; i < 10; i++);
  105.     NFADDR = (page >> 8) &0xff;
  106.     for (i = 0; i < 10; i++);
  107.     NFADDR = (page >> 16) &0xff;
  108.     for (i = 0; i < 10; i++);
  109. }

  110. void nand_wait_ready(void)
  111. {
  112.     while (!(NFSTAT & 1));
  113. }

  114. unsigned char nand_data(void)
  115. {
  116.     return NFDATA;
  117. }

  118. void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
  119. {
  120.     int col = addr % 2048;
  121.     int i = 0;
  122.     
  123.     /* 1.选中 */
  124.     nand_select();
  125.     while (i < len)
  126.     {
  127.         /* 2.发出读命令00h */
  128.         nand_cmd(0x00);
  129.         /* 3.发出地址(分5个地址周期) */
  130.         nand_addr(addr);
  131.         /* 4.发出读命令30h */
  132.         nand_cmd(0x30);
  133.         /* 5.判断状态 */
  134.         nand_wait_ready();
  135.         /* 6.读数据 */
  136.         for (; (col < 2048) && (i < len); col++)
  137.         {
  138.             buf[i] = nand_data();
  139.             i++;
  140.             addr++;
  141.         }

  142.         col = 0;
  143.     }
  144.     /* 7. 取消选中 */
  145.     nand_deselect();
  146. }

  147. #define PCLK 50000000 // init.c中的clock_init函数设置PCLK为50MHz
  148. #define UART_CLK PCLK // UART0的时钟源设为PCLK
  149. #define UART_BAUD_RATE 115200 // 波特率
  150. #define UART_BRD ((UART_CLK / (UART_BAUD_RATE * 16)) - 1)

  151. /*
  152.  * 初始化UART0
  153.  * 115200,8N1, 无流控
  154.  */
  155. void uart0_init(void)
  156. {
  157.     GPHCON |= 0xa0; /* GPH2,GPH3用作TXD0,RXD0 */
  158.     GPHUP = 0x0c; /* GPG2,GPH3内部上拉 */

  159.     ULCON0 = 0x03; /* 8N1 */
  160.     UCON0 = 0x05; /* 查询方式,UART时钟源为PCLK */
  161.     UFCON0 = 0x00; /* 不使用FIFO */
  162.     UMCON0 = 0x00; /* 不使用流控 */
  163.     UBRDIV0 = UART_BRD; /* 波特率为115200 */
  164. }

  165. /*
  166.  * 发送一个字节
  167.  */
  168. void putc(unsigned char c)
  169. {
  170.     /* 等待,直到发送缓冲区中的数据已经全部发送出去 */
  171.     while (!(UTRSTAT0 & TXD0READY));

  172.     /* 向UTXH0寄存器中写入数据,UART即自动将它发送出去 */
  173.     UTXH0 = c;
  174. }

  175. void puts( char *str)
  176. {
  177.     int i = 0;
  178.     while (str[i])
  179.         {
  180.         putc(str[i]);
  181.         i++;
  182.         }
  183. }

  184. void puthex(unsigned int val)
  185. {
  186.     /* 0x1234abcd */
  187.     int i;
  188.     int j;

  189.     puts("0x");

  190.     for (i = 0; i< 8; i++)

  191.     {
  192.         j = (val >> ((7-i)*4)) & 0xf;
  193.         if((j>=0 ) && (j<=9))
  194.             putc('0' + j);
  195.         else
  196.             putc('A' + j -0xa);
  197.     }
  198. }
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

点击(此处)折叠或打开

  1. #include "setup.h"

  2. extern void uart0_init(void);
  3. extern void nand_read(unsigned int addr, unsigned char *buf, unsigned int len);
  4. extern void putc(unsigned char c);
  5. extern void puts(char *str);
  6. extern void puthex(unsigned int val);

  7. static struct tag *params;

  8. void setup_start_tag(void)
  9. {
  10.    // params = (struct tag *) bd->bi_boot_params; /* bd->bi_boot_params值在移植好的源码中搜索直接用 */
  11.    params = (struct tag *)0x30000100; /* 该值可以在移植好的u-boot里面找到。 */

  12.     params->hdr.tag = ATAG_CORE;
  13.     params->hdr.size = tag_size (tag_core);

  14.     params->u.core.flags = 0;
  15.     params->u.core.pagesize = 0;
  16.     params->u.core.rootdev = 0;

  17.     params = tag_next(params);
  18. }

  19. void setup_memory_tags(void)
  20. {
  21.     params->hdr.tag = ATAG_MEM;
  22.     params->hdr.size = tag_size (tag_mem32);

  23.    // params->u.mem.start = bd->bi_dram[i].start;
  24.    params->u.mem.start = 0x30000000;
  25.    // params->u.mem.size = bd->bi_dram[i].size;
  26.    params->u.mem.size = 0x4000000;

  27.     params = tag_next(params);
  28. }

  29. int strlen(char *str)
  30. {
  31.     int i = 0;
  32.     while (str[i])
  33.         {
  34.             i++;
  35.         }
  36.     return i;
  37. }

  38. void strcpy(char *dest, char *src)
  39. {
  40. // char *tmp =dest;

  41.     while ((*dest++ = *src++) != '\0');
  42. }

  43. void setup_commandline_tag(char *cmdline)
  44. {
  45.     int len = strlen(cmdline) + 1;
  46.     params->hdr.tag = ATAG_CMDLINE;
  47.    // params->hdr.size =
  48.      // (sizeof (struct tag_header) + strlen(p) + 1 + 4) >> 2;
  49.     params->hdr.size = (sizeof (struct tag_header) + len + 3) >> 2; /* 向4取整 */
  50.     strcpy (params->u.cmdline.cmdline, cmdline);
  51.     params = tag_next(params);
  52. }

  53. void setup_end_tag(void)
  54. {
  55.     params->hdr.tag = ATAG_NONE;
  56.     params->hdr.size = 0;
  57. }

  58. int main(void)
  59. {
  60.     void (*theKernel)(int zero, int arch, unsigned int params);
  61.     volatile unsigned int *p = (volatile unsigned int *)0x30008000;
  62.     /* 0.设置串口:内核启动时会从串口打印一些信息,但一开始内核没有初始化串口 */
  63.     uart0_init();
  64.     /* 1.从nand flash中把内核读入内存 */
  65.     puts("Copy kernel from nand \n\r");
  66.     nand_read(0x00120000 , (unsigned char *)0x30008000, 0x400000); /* 0x00120000这个地址要看之前的kernel分区,如果是烧uImage,还要加一个64字节的头 */
  67.                                                       /* 从0x00120000这个地址读到0x30008000,大小为0x400000 */
  68.     puthex(0x1234ABCD);
  69.     puts("\n\r");
  70.     puthex(*p);
  71.     puts("\n\r");

  72.     /* 2.设置参数 */
  73.     puts("Set boot params\n\r");
  74.     setup_start_tag();
  75.     setup_memory_tags();
  76.     setup_commandline_tag("noinitrd root=/dev/mtdblock3 rootfstype=yaffs2 init=/linuxrc console=ttySAC0");
  77.     setup_end_tag();
  78.     
  79.     /* 3.跳转执行 */
  80.     puts("Boot kernel\n\r");
  81.     theKernel = (void (*)(int, int, unsigned int ))0x30008000;
  82.     theKernel(0, 362, 0x30000100);
  83.     /*
  84.      * mov pc,#0x30008000
  85.      * ldr r1, =362
  86.      * ldr r2, =0x30000100
  87.      * mov pc, #0x30008000
  88.      */

  89.     puts("Error!\n\r");
  90.     /* 如果一切正常,不会执行到这里 */

  91.     return -1;
  92. }
这个函数大部分是参照u-boot中的相关函数写的。主要是几个tag的写法,看下tag的下定义

点击(此处)折叠或打开

  1. struct tag {
  2.     struct tag_header hdr;
  3.     union {
  4.         struct tag_core        core;
  5.         struct tag_mem32    mem;
  6.         struct tag_videotext    videotext;
  7.         struct tag_ramdisk    ramdisk;
  8.         struct tag_initrd    initrd;
  9.         struct tag_serialnr    serialnr;
  10.         struct tag_revision    revision;
  11.         struct tag_videolfb    videolfb;
  12.         struct tag_cmdline    cmdline;

  13.         /*
  14.          * Acorn specific
  15.          */
  16.         struct tag_acorn    acorn;

  17.         /*
  18.          * DC21285 specific
  19.          */
  20.         struct tag_memclk    memclk;
  21.     } u;
  22. };
每一个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了。











阅读(3005) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~