三,移植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启动了!