Chinaunix首页 | 论坛 | 博客
  • 博客访问: 831167
  • 博文数量: 281
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2770
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-02 19:45
个人简介

邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛

文章分类
文章存档

2020年(1)

2018年(1)

2017年(56)

2016年(72)

2015年(151)

分类: 嵌入式

2015-07-06 14:12:03

韦东山老师讲解自己动手写bootloader的笔记记录:


看一看几个相关文件:

start.S文件:启动流程代码

bootloader启动的第一阶段:

1. 关闭看门狗

2. 设置时钟频率,设置分频系数等;

3. 初始化SDRAM, 因为需要快速启动,所以需要将代码从flash上拷贝到内存SDRAM中启动

4. 代码重定向 (注意nor启动和nand启动的不同点)

5. 执行main函数,跳转到启动的第二阶段。



  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
  15.     str r1, [r0]

  16.     /* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */
  17.     mrc p15, 0, r1, c1, c0, 0       /* 读出控制寄存器 */
  18.     orr r1, r1, #0xc0000000         /* 设置为“asynchronous bus mode” */
  19.     mcr p15, 0, r1, c1, c0, 0       /* 写入控制寄存器 */

  20.     /* MPLLCON = S3C2440_MPLL_200MHZ */
  21.     ldr r0, =0x4c000004
  22.     ldr r1, =S3C2440_MPLL_400MHZ
  23.     str r1, [r0]

  24.     /* 启动ICACHE, 开启ICACHE将加快启动速度 */
  25.     mrc p15, 0, r0, c1, c0, 0 @ read control reg
  26.     orr r0, r0, #(1<<12)
  27.     mcr p15, 0, r0, c1, c0, 0 @ write it back


  28. /* 3. 初始化SDRAM */
  29.     ldr r0, =MEM_CTL_BASE
  30.     adr r1, sdram_config /* sdram_config的当前地址 */
  31.     add r3, r0, #(13*4)
  32. 1:
  33.     ldr r2, [r1], #4
  34.     str r2, [r0], #4
  35.     cmp r0, r3
  36.     bne 1b

  37. /* 4. 重定位 : 把bootloader本身的代码从flash复制到它的链接地址去 */
  38.     ldr sp, =0x34000000 /* 将SP放在SDRAM的最高地址处,堆栈式满递减堆栈。在调用C函数之前,必须设置好堆栈 */

  39.     bl nand_init

  40.     mov r0, #0
  41.     ldr r1, =_start             /* _start是起始地址,是链接地址, 由链接文件.lds 指定 */
  42.     ldr r2, =__bss_start        /* __bss_start 同样是链接地址,在链接文件中指定 */
  43.     sub r2, r2, r1


  44. /*
  45. * copy_code_to_sdram: 被调用的C函数,
  46. * 第一个参数是R0:表示拷贝数据的源地址,这里从0地址(源地址)处拷贝数据到链接地址(真正运行的地址,也就是目的地址)
  47. * 第二个参数是R1:表示拷贝数据的目的地址,就是链接地址
  48. * 第三个参数是R2:表示拷贝数据的长度 = bss段起始地址 - 链接的起始地址。(都在链接文件中定义。)
  49. */
  50.     bl copy_code_to_sdram


  51. /*
  52. * 由上面的拷贝代码知道,在bss段中的数据并没有拷贝到内存当中去,这是因为这些数据初始化都是0,所以只需要在初始化的时候,显示的把bss段清零,就OK了。
  53. */
  54.     bl clear_bss



  55.     
  56. /* 5. 执行main */
  57.     ldr lr, =halt
  58.     ldr pc, =main
  59. halt:
  60.     b halt



  61. sdram_config:
  62.     .long 0x22011110 //BWSCON
  63.     .long 0x00000700 //BANKCON0
  64.     .long 0x00000700 //BANKCON1
  65.     .long 0x00000700 //BANKCON2
  66.     .long 0x00000700 //BANKCON3
  67.     .long 0x00000700 //BANKCON4
  68.     .long 0x00000700 //BANKCON5
  69.     .long 0x00018005 //BANKCON6
  70.     .long 0x00018005 //BANKCON7
  71.     .long 0x008C04F4 // REFRESH
  72.     .long 0x000000B1 //BANKSIZE
  73.     .long 0x00000030 //MRSRB6
  74.     .long 0x00000030 //MRSRB7





链接文件 boot.lds:

  1. SECTIONS {
  2.     . = 0x33f80000;
  3.     .text : { *(.text) }
  4.     
  5.     . = ALIGN(4);
  6.     .rodata : {*(.rodata*)}
  7.     
  8.     . = ALIGN(4);
  9.     .data : { *(.data) }
  10.     
  11.     . = ALIGN(4);
  12.     __bss_start = .;
  13.     .bss : { *(.bss) *(COMMON) }
  14.     __bss_end = .;
  15. }

3. Makefile

  1. CC = arm-linux-gcc
  2. LD = arm-linux-ld
  3. AR = arm-linux-ar
  4. OBJCOPY = arm-linux-objcopy
  5. OBJDUMP = arm-linux-objdump

  6. CFLAGS         := -Wall -O2
  7. CPPFLAGS     := -nostdinc -nostdlib -fno-builtin

  8. objs := start.o init.o boot.o

  9. boot.bin: $(objs)
  10.     ${LD} -Tboot.lds -o boot.elf $^
  11.     ${OBJCOPY} -O binary -S boot.elf $@
  12.     ${OBJDUMP} -D -m arm boot.elf > boot.dis
  13.     
  14. %.o:%.c
  15.     ${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

  16. %.o:%.S
  17.     ${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

  18. clean:
  19.     rm -f *.o *.bin *.elf *.dis

4. 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 puts(char *str);
  5. extern void puthex(unsigned int val);


  6. static struct tag *params;

  7. void setup_start_tag(void)
  8. {
  9.     params = (struct tag *)0x30000100; //这里设置了内核参数放置的位置为:0x30000100

  10.     params->hdr.tag = ATAG_CORE;
  11.     params->hdr.size = tag_size (tag_core);

  12.     params->u.core.flags = 0;
  13.     params->u.core.pagesize = 0;
  14.     params->u.core.rootdev = 0;

  15.     params = tag_next (params);
  16. }

  17. void setup_memory_tags(void)
  18. {
  19.     params->hdr.tag = ATAG_MEM;
  20.     params->hdr.size = tag_size (tag_mem32);
  21.     
  22.     params->u.mem.start = 0x30000000;    // 表示起始地址
  23.     params->u.mem.size = 64*1024*1024;   // 表示内存的大小,这里为64MB
  24.     
  25.     params = tag_next (params);
  26. }

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

  36. void strcpy(char *dest, char *src)
  37. {
  38.     while ((*dest++ = *src++) != '\0');
  39. }

  40. void setup_commandline_tag(char *cmdline)
  41. {
  42.     int len = strlen(cmdline) + 1;
  43.     
  44.     params->hdr.tag = ATAG_CMDLINE;
  45.     params->hdr.size = (sizeof (struct tag_header) + len + 3) >> 2;  //注意:这里要四字节对齐方式

  46.     strcpy (params->u.cmdline.cmdline, cmdline);

  47.     params = tag_next (params);
  48. }

  49. void setup_end_tag(void)
  50. {
  51.     params->hdr.tag = ATAG_NONE;
  52.     params->hdr.size = 0;
  53. }


  54. int main(void)
  55. {
  56.     void (*theKernel)(int zero, int arch, unsigned int params);
  57.     volatile unsigned int *p = (volatile unsigned int *)0x30008000;

  58.     /* 0. 帮内核设置串口: 内核启动的开始部分会从串口打印一些信息,但是内核一开始没有初始化串口 */
  59.     uart0_init();
  60.     
  61.     /* 1. 从NAND FLASH里把内核读入内存 */
  62.     puts("Copy kernel from nand\n\r"); 
  63.                                                                   
        // 从nand flash的地址0x60064处拷贝大小为0x200000的代码到内存地址为0x30008000处。即:从nand flash上拷贝内核到SDRAM中。 
  64.     nand_read(0x60000+64, (unsigned char *)0x30008000, 0x200000);
  65.              // 0x30008000为内核需要运行的开始地址(就是链接地址,在SDRAM当中),0x200000为整个内核的长度大小。
  66.              // uImage = 64bytes + zImage; zImage为真正的内核。uImage存在0x60000处。
  67.              // 所以zImage的起始地址为:0x60000 + 64

  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 init=/linuxrc console=ttySAC0"); // 启动的命令参数
  77.     setup_end_tag();

  78.     /* 3. 跳转执行 */
  79.     puts("Boot kernel\n\r");
  80.     theKernel = (void (*)(int, int, unsigned int))0x30008000;
  81.     theKernel(0, 362, 0x30000100); // 向内核启动的函数,传递了三个参数,
  82.                                    // 第一个参数为0
  83.                                    // 第二个参数为362, 为机器号
  84.             // 第三个参数为0x30000100:这个是内核需要的启动参数的起始地址。内核启动需要从这个地方获取所需的启动参                数,比如内存的起始地址,大小等等。
  85.  
  86.     /*
  87.      * mov r0, #0
  88.      * ldr r1, =362
  89.      * ldr r2, =0x30000100
  90.      * mov pc, #0x30008000
  91.      */

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

  94.     return -1;
  95. }




分析这个还是蛮有意思的。启动需要的文件也不多。
主要要清楚知道uboot的工作目的:就是启动内核
1. 从flash上把内核读入到内存中
    引申的功能: 能够读取flash(那么就需要初始化flash)
                       初始化内存,时钟等
2. 启动内核
   a 设置参数(启动时候,需要的参数)
   b 跳转执行

 




最简单的bootloader的编写步骤:
1. 初始化硬件:关看门狗、设置时钟、设置SDRAM、初始化NAND FLASH
2. 如果bootloader比较大,要把它重定位到SDRAM
3. 把内核从NAND FLASH读到SDRAM
4. 设置"要传给内核的参数"
5. 跳转执行内核



改进:
1. 提高CPU频率, 200MHZ ==> 400MHZ
2. 启动ICACHE




 


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