从零写bootloader--第一阶段启动的实现

韦东山视频学习笔记。平台JZ2440。CPU为S3C2440A,NandFlash型号为K9F2G08U0M。本文如果有什么错误的地方,欢迎各位批评指正,不甚感激。

注:第一阶段部分函数未完成,还不能启动内核

一、剖析实现从零写bootloader我们所要做的工作:

        要想从零写出自己的bootloader,我们得先知道bootloader的目的是干嘛。就好像我们做事,得先明确目的,才有具体的行动。

        bootloader的目的:启动内核

        要启动内核,我们得先知道内核在哪?内核很大,通常存储于NandFlash上(较NorFlash:容量大、块小、 擦写快、便宜)。这里我们可以初步           确定我们的工作:

               工作1:从NandFlash上把内核读入内存

                      1、能读NandFlash

                      2、初始化内存

               工作2:启动内核

                      3、设置参数(如:告诉内核我的内存有多大;去哪挂接根文件系统……)

                      4、跳转执行

               工作3:其他

               5、关看门狗;

            6、提高运行速度,初始化时钟(系统上电12M);

           7、清零bss段(存放未初始化的全局变量和初始化为零的全局变量)

                      8、……


        到这里,我们所要做的工作就是完成上述基本工作,那bootloader第一阶段就基本完成了。下面有三个代码实现文件start.s、init.c、boot.ldsstart.s是执行的第一个文件;init.c是一些函数C语言的实现和一些初始化函数,start.s中大部分跳转函数在这里边实现;boot.lds是链接脚本。


二、实现代码


1、start.s


  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.   
  5. .text             //代码段  
  6. .global _start    //全局变量  
  7. _start:  
  8.   
  9. /* 1. 关看门狗 */  
  10.     ldr r0, =0x53000000  
  11.     mov r1, #0  
  12.     str r1, [r0]  
  13.   
  14. /* 2. 设置时钟 */  
  15.     ldr r0, =0x4c000014  
  16.     //  mov r1, #0x03;            // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1  
  17.     mov r1, #0x05;            // FCLK:HCLK:PCLK=1:4:8  
  18.     str r1, [r0]  
  19.   
  20.     //当FCLK != HCLK时,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode  
  21.     /* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */  
  22.     mrc p150, r1, c1, c00       /* 读出控制寄存器 */   
  23.     orr r1, r1, #0xc0000000         /* 设置为“asynchronous bus mode” */  
  24.     mcr p150, r1, c1, c00       /* 写入控制寄存器 */  
  25.   
  26.     /* MPLLCON = S3C2440_MPLL_200MHZ */  
  27.     ldr r0, =0x4c000004  
  28.     ldr r1, =S3C2440_MPLL_400MHZ  
  29.     str r1, [r0]  
  30.   
  31.     /* 启动ICACHE */  
  32.     mrc p150, r0, c1, c00   @ read control reg  
  33.     orr r0, r0, #(1<<12)  
  34.     mcr p150, r0, c1, c00   @ write it back  
  35.   
  36.   
  37. /* 3. 初始化SDRAM */  
  38.     ldr r0, =MEM_CTL_BASE  
  39.     adr r1, sdram_config     /* 得到sdram_config标号的当前地址 */  
  40.     add r3, r0, #(13*4)        //r3 = r0 + 52  
  41. 1:  
  42.     ldr r2, [r1], #4         //从r1所指的地方取一个值,存到r2,然后r1加4  
  43.     str r2, [r0], #4         //把r2的值存到r0的地址上,然后r0加4  
  44.     cmp r0, r3  
  45.     bne 1b                   //b表示back:表示前面的1标号;可以有多个1标号(1f:forward)  
  46.   
  47. /* 4. 重定位 : 把bootloader本身的代码从flash复制到它的链接地址去 */  
  48.     ldr sp, =0x34000000     //我们内存是64M,基地址是0x30000000,到0x34000000是64M大小,指向最高内存(栈是向下增长的)  
  49.   
  50.     /* 不管是NandFlash启动还是NorFlash启动,内核都存在NandFlash上,所以都得先初始化NandFlash */  
  51.     bl nand_init  
  52.   
  53.     /*汇编向C函数传递参数(参数1:r0、参数2:r1、参数3:r2)*/  
  54.     mov r0, #0             //源地址  
  55.     ldr r1, =_start        //目的地址(即需要拷贝到内存中的链接地址)  
  56.     ldr r2, =__bss_start  
  57.     sub r2, r2, r1         //长度r2 = r2 - r1  
  58.       
  59.     bl copy_code_to_sdram  //接受r0、r1、r2三个参数给函数  
  60.   
  61.     /*拷贝完后,将未初始化的全局和初始化为零的全局内存空间清零*/      
  62.     bl clear_bss  
  63.       
  64. /* 5. 执行main */  
  65.     ldr lr, =halt  
  66.     ldr pc, =main  
  67. halt:  
  68.     b halt  
  69.   
  70. sdram_config:  
  71.     .long 0x22011110     //BWSCON  
  72.     .long 0x00000700     //BANKCON0  
  73.     .long 0x00000700     //BANKCON1  
  74.     .long 0x00000700     //BANKCON2  
  75.     .long 0x00000700     //BANKCON3    
  76.     .long 0x00000700     //BANKCON4  
  77.     .long 0x00000700     //BANKCON5  
  78.     .long 0x00018005     //BANKCON6  
  79.     .long 0x00018005     //BANKCON7  
  80.     .long 0x008C04F4     // REFRESH  
  81.     .long 0x000000B1     //BANKSIZE  
  82.     .long 0x00000030     //MRSRB6  
  83.     .long 0x00000030     //MRSRB7  


2、init.c


  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.   
  9. /* GPIO */  
  10. #define GPHCON              (*(volatile unsigned long *)0x56000070)  
  11. #define GPHUP               (*(volatile unsigned long *)0x56000078)  
  12.   
  13. /* UART registers*/  
  14. #define ULCON0              (*(volatile unsigned long *)0x50000000)  
  15. #define UCON0               (*(volatile unsigned long *)0x50000004)  
  16. #define UFCON0              (*(volatile unsigned long *)0x50000008)  
  17. #define UMCON0              (*(volatile unsigned long *)0x5000000c)  
  18. #define UTRSTAT0            (*(volatile unsigned long *)0x50000010)  
  19. #define UTXH0               (*(volatile unsigned char *)0x50000020)  
  20. #define URXH0               (*(volatile unsigned char *)0x50000024)  
  21. #define UBRDIV0             (*(volatile unsigned long *)0x50000028)  
  22.   
  23. #define TXD0READY   (1<<2)  
  24.   
  25.   
  26. void nand_read(unsigned int addr, unsigned charchar *buf, unsigned int len);  
  27.   
  28. /* 
  29. * 判断方法:像零地址写数据 
  30. * NorFlash启动时从NorFlash零地址开始执行,向零地址写数据就是往NorFlash上写数据,会失败(Norflash可以像内存一样读,但不能像内存一样写) 
  31. * NandFlash启动时,零地址为片内内存,向内存写数据,读出后会写成功。 
  32. */  
  33. int isBootFromNorFlash(void)  
  34. {  
  35.     volatile intint *p = (volatile intint *)0;  
  36.     int val;  
  37.   
  38.     val = *p;  
  39.     *p = 0x12345678;  
  40.     if (*p == 0x12345678)  
  41.     {  
  42.         /* 写成功, 是nand启动 */  
  43.         *p = val;  
  44.         return 0;  
  45.     }  
  46.     else  
  47.     {  
  48.         /* NOR不能像内存一样写 */  
  49.         return 1;  
  50.     }  
  51. }  
  52.   
  53. void copy_code_to_sdram(unsigned charchar *src, unsigned charchar *dest, unsigned int len)  
  54. {     
  55.     int i = 0;  
  56.       
  57.     /* 如果是NOR启动 */  
  58.     if (isBootFromNorFlash())  
  59.     {  
  60.         while (i < len)  
  61.         {  
  62.             dest[i] = src[i];  
  63.             i++;  
  64.         }  
  65.     }  
  66.     else  
  67.     {  
  68.         //nand_init();  
  69.         nand_read((unsigned int)src, dest, len);  
  70.     }  
  71. }  
  72.   
  73. /* C语言如何引用__bss_start等标号的典型实例 
  74. ******************************************** 
  75.  * 汇编中引用: 
  76.  *      ldr r1, =_start        //目的地址(即需要拷贝到内存中的链接地址) 
  77.  *      ldr r2, =__bss_start 
  78.  *      sub r2, r2, r1         //长度r2 = r2 - r1 
  79.  */  
  80. void clear_bss(void)  
  81. {  
  82.     extern int __bss_start, __bss_end;  
  83.     intint *p = &__bss_start;  
  84.       
  85.     for (; p < &__bss_end; p++)  
  86.         *p = 0;  
  87. }  
  88.   
  89. void nand_init(void)  
  90. {  
  91. #define TACLS   0  
  92. #define TWRPH0  1  
  93. #define TWRPH1  0  
  94.     /* 设置时序 */  
  95.     NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);  
  96.     /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */  
  97.     NFCONT = (1<<4)|(1<<1)|(1<<0);      
  98. }  
  99.   
  100. void nand_select(void)  
  101. {  
  102.     NFCONT &= ~(1<<1);      
  103. }  
  104.   
  105. void nand_deselect(void)  
  106. {  
  107.     NFCONT |= (1<<1);   
  108. }  
  109.   
  110. void nand_cmd(unsigned char cmd)  
  111. {  
  112.     volatile int i;  
  113.     NFCMMD = cmd;  
  114.     for (i = 0; i < 10; i++);  
  115. }  
  116.   
  117. void nand_addr(unsigned int addr)  
  118. {  
  119.     unsigned int col  = addr % 2048;  
  120.     unsigned int page = addr / 2048;  
  121.     volatile int i;  
  122.   
  123.     NFADDR = col & 0xff;  
  124.     for (i = 0; i < 10; i++);  
  125.     NFADDR = (col >> 8) & 0xff;  
  126.     for (i = 0; i < 10; i++);  
  127.       
  128.     NFADDR  = page & 0xff;  
  129.     for (i = 0; i < 10; i++);  
  130.     NFADDR  = (page >> 8) & 0xff;  
  131.     for (i = 0; i < 10; i++);  
  132.     NFADDR  = (page >> 16) & 0xff;  
  133.     for (i = 0; i < 10; i++);      
  134. }  
  135.   
  136. void nand_wait_ready(void)  
  137. {  
  138.     while (!(NFSTAT & 1));  
  139. }  
  140.   
  141. unsigned char nand_data(void)  
  142. {  
  143.     return NFDATA;  
  144. }  
  145.   
  146. void nand_read(unsigned int addr, unsigned charchar *buf, unsigned int len)  
  147. {  
  148.     int col = addr % 2048;  
  149.     int i = 0;  
  150.           
  151.     /* 1. 选中 */  
  152.     nand_select();  
  153.   
  154.     while (i < len)  
  155.     {  
  156.         /* 2. 发出读命令00h */  
  157.         nand_cmd(0x00);  
  158.   
  159.         /* 3. 发出地址(分5步发出) */  
  160.         nand_addr(addr);  
  161.   
  162.         /* 4. 发出读命令30h */  
  163.         nand_cmd(0x30);  
  164.   
  165.         /* 5. 判断状态(等待就绪),读操作需要一定时间,我们要等待读完后再进行其他操作 */  
  166.         nand_wait_ready();  
  167.   
  168.         /* 6. 读数据 */  
  169.         for (; (col < 2048) && (i < len); col++)  
  170.         {  
  171.             buf[i] = nand_data();  
  172.             i++;  
  173.             addr++;  
  174.         }  
  175.           
  176.         col = 0;  
  177.     }  
  178.   
  179.     /* 7. 取消选中 */         
  180.     nand_deselect();  
  181. }  
  182.   
  183. #define PCLK            50000000    // init.c中的clock_init函数设置PCLK为50MHz  
  184. #define UART_CLK        PCLK        //  UART0的时钟源设为PCLK  
  185. #define UART_BAUD_RATE  115200      // 波特率  
  186. #define UART_BRD        ((UART_CLK  / (UART_BAUD_RATE * 16)) - 1)  
  187.   
  188. /* 
  189.  * 初始化UART0 
  190.  * 115200,8N1,无流控 
  191.  */  
  192. void uart0_init(void)  
  193. {  
  194.     GPHCON  |= 0xa0;    // GPH2,GPH3用作TXD0,RXD0  
  195.     GPHUP   = 0x0c;     // GPH2,GPH3内部上拉  
  196.   
  197.     ULCON0  = 0x03;     // 8N1(8个数据位,无较验,1个停止位)  
  198.     UCON0   = 0x05;     // 查询方式,UART时钟源为PCLK  
  199.     UFCON0  = 0x00;     // 不使用FIFO  
  200.     UMCON0  = 0x00;     // 不使用流控  
  201.     UBRDIV0 = UART_BRD; // 波特率为115200  
  202. }  
  203.   
  204. /* 
  205.  * 发送一个字符 
  206.  */  
  207. void putc(unsigned char c)  
  208. {  
  209.     /* 等待,直到发送缓冲区中的数据已经全部发送出去 */  
  210.     while (!(UTRSTAT0 & TXD0READY));  
  211.       
  212.     /* 向UTXH0寄存器中写入数据,UART即自动将它发送出去 */  
  213.     UTXH0 = c;  
  214. }  
  215.   
  216. void puts(charchar *str)  
  217. {  
  218.     int i = 0;  
  219.     while (str[i])  
  220.     {  
  221.         putc(str[i]);  
  222.         i++;  
  223.     }  
  224. }  
  225.   
  226. void puthex(unsigned int val)  
  227. {  
  228.     /* 0x1234abcd */  
  229.     int i;  
  230.     int j;  
  231.       
  232.     puts("0x");  
  233.   
  234.     for (i = 0; i < 8; i++)  
  235.     {  
  236.         j = (val >> ((7-i)*4)) & 0xf;  
  237.         if ((j >= 0) && (j <= 9))  
  238.             putc('0' + j);  
  239.         else  
  240.             putc('A' + j - 0xa);  
  241.           
  242.     }  
  243.       
  244. }  


3、boot.lds


  1. SECTIONS {  
  2.     . = 0x33f80000//跟最大地址刚好相差512K,我们的程序肯定不会超过512K,所以选该地址  
  3.     .text : { *(.text) }  
  4.       
  5.     . = ALIGN(4);  
  6.     .rodata : {*(.rodata*)}   
  7.       
  8.     . = ALIGN(4);  
  9.     .data : { *(.data) }  
  10.   
  11.         /*bss段存放没有初始化的全局变量或则初始化为0的全局变量*/  
  12.         //我们编译出来的二进制文件不会含有这些初始化为0的变量  
  13.         //因为bss不是文件里的东西,那可知代码段大小(文件大小) = (__bss_start - _start(0x33f80000))  
  14.     . = ALIGN(4);  
  15.     __bss_start = .;  
  16.     .bss : { *(.bss)  *(COMMON) }  
  17.     __bss_end = .;  

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值