三,移植uboot-支持NAND启动

文档时间:2018-08-10

交叉编译器:arm-linux-gcc-4.3.2

Ubuntu版本:16.04

uboot版本:2013.10

 

1,分析为何不能从 nand 启动

将上一章编译好的 u-boot.bin 烧写到 nand flash 中,将板子设置位 nand 启动,发现串口无任何输出,说明 nand 启动未成功,查看反汇编文件 u-boot.dis

先生成 u-boot.dis:

arm-linux-objdump -D u-boot > u-boot.dis

 打开 u-boot.dis :

发现 u-boot 如果从 nand 启动,有问题,主要是因为 s3c2440 sram 太小,只有 4k,而在前 4k 地址中,调用了很多 4k 地址以外的函数,这样就会导致程序跑飞

因此,为了能够让 uboot 支持 nand 启动,需要在前 4k 就完成代码重定位和代码拷贝的工作

2,修改代码实现 nand 启动

1),参考裸机程序关于 nand 部分和代码拷贝部分,添加 nand_read_ll.c 文件

在 board/samsung/jz2440 目录下新建 nand_read_ll.c 文件,将以前裸机程序中关于 nand 部分和 代码拷贝部分的代码复制到此文件中,完成后,代码如下:

  1 /**
  2 * u-boot-2012.04.01_100ask/board/samsung/smdk2440/init.c
  3 **/
  4 
  5 
  6 /* NAND FLASH控制器 */
  7 #define NFCONF (*((volatile unsigned long *)0x4E000000))
  8 #define NFCONT (*((volatile unsigned long *)0x4E000004))
  9 #define NFCMMD (*((volatile unsigned char *)0x4E000008))
 10 #define NFADDR (*((volatile unsigned char *)0x4E00000C))
 11 #define NFDATA (*((volatile unsigned char *)0x4E000010))
 12 #define NFSTAT (*((volatile unsigned char *)0x4E000020))
 13 
 14 
 15 void nand_read_ll(unsigned int addr, unsigned char *buf, unsigned int len);
 16 
 17 static void nand_init_ll(void)
 18 {
 19 #define TACLS   0
 20 #define TWRPH0  1
 21 #define TWRPH1  0
 22     /* 设置时序 */
 23     NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
 24     /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */
 25     NFCONT = (1<<4)|(1<<1)|(1<<0);    
 26 }
 27 
 28 static int isBootFromNorFlash(void)
 29 {
 30     volatile int *p = (volatile int *)0;
 31     int val;
 32 
 33     val = *p;
 34     *p = 0x12345678;
 35     if (*p == 0x12345678)
 36     {
 37         /* 写成功, 是nand启动 */
 38         *p = val;
 39         return 0;
 40     }
 41     else
 42     {
 43         /* NOR不能像内存一样写 */
 44         return 1;
 45     }
 46 }
 47 
 48 void copy_code_to_sdram(volatile unsigned int *src, volatile unsigned int *dest, unsigned int len)
 49 {    
 50     unsigned int i = 0;
 51     
 52     /* 如果是NOR启动 */
 53     if (isBootFromNorFlash())
 54     {
 55         while (i < len)
 56         {
 57             *dest++ = *src++;
 58             i += 4;
 59         }
 60     }
 61     else
 62     {
 63         nand_init_ll();
 64         nand_read_ll((unsigned int)src, (unsigned char *)dest, len);
 65     }
 66 }
 67 
 68 void clear_bss(void)
 69 {
 70     extern int __bss_start, _bss_end;
 71     int *p = &__bss_start;
 72     
 73     for (; p < &_bss_end; p++)
 74         *p = 0;
 75 }
 76 
 77 static void nand_select(void)
 78 {
 79     NFCONT &= ~(1<<1);    
 80 }
 81 
 82 static void nand_deselect(void)
 83 {
 84     NFCONT |= (1<<1);    
 85 }
 86 
 87 static void nand_cmd(unsigned char cmd)
 88 {
 89     volatile int i;
 90     NFCMMD = cmd;
 91     for (i = 0; i < 10; i++);
 92 }
 93 
 94 static void nand_addr_byte(unsigned char addr)
 95 {
 96     volatile int i;
 97     
 98     NFADDR = addr;
 99     for (i = 0; i < 10; i++);    
100 }
101 
102 static void nand_wait_ready(void)
103 {
104     while (!(NFSTAT & 1));
105 }
106 
107 static unsigned char nand_r_data(void)
108 {
109     return NFDATA;
110 }
111 
112 void nand_read_ll(unsigned int addr, unsigned char *buf, unsigned int len)
113 {
114     int i = 0;
115     unsigned int page = addr/2048;
116     unsigned int col = addr&(2048-1);
117         
118     /* 1. 选中 */
119     nand_select();
120 
121     while (i < len)
122     {
123         /* 2. 发出读命令00h */
124         nand_cmd(0x00);
125 
126         /* 3. 发出地址(分5步发出) */
127         nand_addr_byte(col&0xff);
128         nand_addr_byte((col>>8)&0xff);
129 
130         nand_addr_byte(page&0xff);
131         nand_addr_byte((page>>8)&0xff);
132         nand_addr_byte((page>>16)&0xff);
133 
134         /* 4. 发出读命令30h */
135         nand_cmd(0x30);
136 
137         /* 5. 判断状态 */
138         nand_wait_ready();
139 
140         /* 6. 读数据 */
141         for (; (col < 2048) && (i < len); col++)
142         {
143             buf[i++] = nand_r_data();
144         }
145         if(i==len)
146             break;
147         
148         col = 0;
149         page++;
150     }
151 
152     /* 7. 取消选中 */        
153     nand_deselect();
154 }

在 board/samsung/jz2440/Makefile 中增加对 nand_read_ll.c 文件的编译支持(红色部分为修改代码):

COBJS    := jz2440.o
COBJS    += nand_read_ll.o
SOBJS    := lowlevel_init.o

 

 2),修改 include/configs/jz2440.h 文件

查找 CONFIG_SYS_TEXT_BASE 将其值改为:

#define CONFIG_SYS_TEXT_BASE    0x33f00000

 

 0x33f00000是 uboot 重定位之后的地址,从0x33f00000到0x34000000,给uboot预留了1M的存储空间

3),修改 arch/arm/lib/crt0.S 文件

从之前的 arch/arm/cpu/arm920t/start.S 文件知道,程序最后会跳到 _main 处,如下所示:

而 _main 的入口在 crt0.S 中,调用 C 函数也是在 crt0.S 中,因此我们需要修改 crt0.S 文件,修改代码如下(红色为修改部分,蓝色部分为删除代码):

#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
    ldr    sp, =(CONFIG_SPL_STACK)
#else
    ldr    sp, =(CONFIG_SYS_INIT_SP_ADDR)
#endif
    bic    sp, sp, #7    /* 8-byte alignment for ABI compliance */
    sub    sp, #GD_SIZE    /* allocate one GD above SP */
    bic    sp, sp, #7    /* 8-byte alignment for ABI compliance */
    mov    r9, sp        /* GD is above SP */

 

__TEXT_BASE:
  .word CONFIG_SYS_TEXT_BASE

mov r0,#0 //r0对应第一个参数:源地址 ldr r1,__TEXT_BASE //_TEXT_BASE : 0x33f00000:目的地址 ldr r2,__bss_start   // __bss_start - _start (有效代码大小) sub r2, r2, r1
   bl copy_code_to_sdram //该函数首先会初始化nand控制器,然后复制代码到SDRAM连接地址dest上 bl clear_bss //清除bss段 ldr pc, =call_board_init_f //绝对跳转,跳到SDRAM中执行 call_board_init_f: mov r0, #0 bl board_init_f ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */ bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ ldr r9, [r9, #GD_BD] /* r9 = gd->bd */ sub r9, r9, #GD_SIZE /* new GD is below bd */ ldr r1, __TEXT_BASE /* dest_addr */ //第二个参数目的地址 /* call board_init_r */ bl board_init_r /* 进入第二阶段 */
#if ! defined(CONFIG_SPL_BUILD) /* * Set up intermediate environment (new sp and gd) and call * relocate_code(addr_moni). Trick here is that we'll return * 'here' but relocated. */ ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */ bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ ldr r9, [r9, #GD_BD] /* r9 = gd->bd */ sub r9, r9, #GD_SIZE /* new GD is below bd */ adr lr, here ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */ add lr, lr, r0 ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */ b relocate_code here: /* Set up final (full) environment */ bl c_runtime_cpu_setup /* we still call old routine here */ ldr r0, =__bss_start /* this is auto-relocated! */ ldr r1, =__bss_end /* this is auto-relocated! */ mov r2, #0x00000000 /* prepare zero to clear BSS */ clbss_l:cmp r0, r1 /* while not at end of BSS */ strlo r2, [r0] /* clear 32-bit BSS word */ addlo r0, r0, #4 /* move to next */ blo clbss_l bl coloured_LED_init bl red_led_on /* call board_init_r(gd_t *id, ulong dest_addr) */ mov r0, r9 /* gd_t */ ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */ /* call board_init_r */ ldr pc, =board_init_r /* this is auto-relocated! */ /* we should not return here. */ #endif

(PS:上述修改文件中用 __TEXT_BASE 代替 CONFIG_SYS_TEXT_BASE 宏,是因为__TEXT_BASE 定义在前,而宏是在 jz2440.h 文件中定义,为了让代码编译靠前) 

!!!__TEXT_BASE  不能用start.S 中的 _TEXT_BASE 代替!!!

4),修改 arch/arm/lib/board.c 里的 board_init_f 函数(红色部分为修改代码) :

    /*
     * reserve memory for U-Boot code, data & bss
     * round down to next 4 kB limit
     */
    //addr -= gd->mon_len;
    addr = CONFIG_SYS_TEXT_BASE;
    addr &= ~(4096 - 1);

    debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);
unsigned int board_init_f(ulong bootflag)
    memcpy(id, (void *)gd, sizeof(gd_t));

    return ((unsigned int)id);    //将id传给 board_init_r

addr 就是目的地址,因此需要修改为 0x33f00000,如果不修改,原先的栈就会被覆盖,导致代码死掉 

修改 include/common.h 中对 board_init_f 的声明(红色部分为修改代码):

/* arch/$(ARCH)/lib/board.c */
unsigned int    board_init_f(ulong);

 

5),修改链接脚本 u-boot.lds(arch/arm/cpu目录下)

修改 u-boot.lds 将 nand_read_ll.c(实现代码拷贝,代码重定位) lowlevel.S(初始化ADRAM) 放在前面编译,修改如下,红色部分为修改代码:

    . = ALIGN(4);
    .text :
    {
        *(.__image_copy_start)
        CPUDIR/start.o (.text*)
        board/samsung/jz2440/lowlevel.o (.text*)
        board/samsung/jz2440/nand_read_ll.o (.text*)
        *(.text*)
    }

 

修改完毕,编译,出现如下错误:

输入命令 grep "R_ARM_RELATIVE" * -nR 进行搜索,发现 Makefile 中有一个检查重定位的规则,屏蔽该规则,代码如下(红色为修改部分):

# ARM relocations should all be R_ARM_RELATIVE.
checkarmreloc: $(obj)u-boot
#    @if test "R_ARM_RELATIVE" != \
        "`$(CROSS_COMPILE)readelf -r $< | cut -d ' ' -f 4 | grep R_ARM | sort -u`"; \
        then echo "$< contains relocations other than \
        R_ARM_RELATIVE"; false; fi

再次编译,成功,进行烧写(可以先把旧的u-boot.bin烧写到norflash,然后通过旧的uboot把新的uboot烧写到nand flash),得到如下界面,表示启动成功

到此,我们的代码已经支持NOR启动,NAND启动了!

 

posted @ 2018-08-09 21:20  Mango丶  阅读(1598)  评论(0编辑  收藏  举报