移植U-BOOT-2016.11到JZ2440详细教程(4)
让U-BOOT支持从NAND FLASH启动
在移植之前,我们首先要知道ARM开发板从NAND和NOR FLASH启动有什么区别。
关于这块内容的介绍,推荐看一下这个博主的文章,讲的还是蛮具体的,链接: [https://www.geek-share.com/detail/2675531481.html].
这边我们只需要知道如果要从NAND FLASH启动,那么我们在对代码重定位之前的程序不能大于4K。所以在这4K的程序中我们一般要做这些事情:
- 初始化基本底层硬件,如时钟,看门狗,中断等。
- 初始化内存控制器。
- 初始化SDRAM。
- 复制代码到SDRAM。
- 清除BSS段。
- 跳转到SDRAM继续执行。
接着我们就按着上面的顺序一步一步移植代码。
取消链接时U-BOOT提供的PIE选项
在链接的时候,如果我们启用了PIE功能,那么程序中的变量就会被抽出来放到代码段后面的一些特殊段,而这些变量的地址肯定超过了4K,无法再访问了。所以我们要去除这个PIE功能。
首先直接用 “#”注释掉pie选项:
arch/arm/config.mk,第89行
# LDFLAGS_u-boot += -pie
然后2016版的U-BOOT和韦东山老师移植的2012版有一个区别就是在第111行添加了一个检查ARM相对位置的选项,因为我们已经警用了pie选项,所以我们也要把这个注释掉,不然编译会报错。修改后如下:
# ALL-y += checkarmreloc
现在关于pie就改好了,在后续程序中我们就不需要在重定位代码后再去计算变量的相对位置。
指定链接地址
关于链接地址,运行地址等,推荐看一下这篇文章,链接: [https://www.geek-share.com/detail/2658719661.html].这边我们只需要知道运行地址和链接地址最好要一致,不然程序会出错跑飞。但是有一种典型的例外就是u-boot从NAND FLASH启动的过程。
在链接U-BOOT的时候我们往往把他的链接地址设为SDRAM的地址,虽然指定了链接地址,但是程序自己不知道现在是运行在哪个地址。所以开发板一上电,程序正常的从CPU片内0地址开始运行,当我们把所有的准备工作都做好后,通过一条跳转指令跳转到链接地址,程序就从SDRAM上接着往下执行。注意在这个过程中不能使用位置有关码,至于什么是位置有关,什么是位置无关推荐上一看下上面那篇文章。
S3C2440的SDRAM的起始地址是0x30000000,我们接的片外SDRAM的大小为64M(0x4000000),可以计算出SDRAM的地址为0x30000000~0x34000000。我们给u-boot 1Mb的空间来存放。所以修改代码如下:
include/configs/smdk2440.h,第24行
#define CONFIG_SYS_TEXT_BASE 0x33F00000
在原本的程序中,u-boot的存放首地址是根据SDRAM减去u-boot的大小计算出来了,因为我们现在已经指定了u-boot的链接地址,所以也要修改分配给u-boot的起始地址。
\common\board_f.c,第520行
//gd->relocaddr -= gd->mon_len; //gd->relocaddr &= ~(4096 - 1); gd->relocaddr = (unsigned int)_start; //指定uboot空间从_start开始
初始化内存控制器及NAND FLASH
因为我们使用了跟卫东山老师同样的时钟和分频系数,所以直接可以使用他的内存控制器配置参数。
\board\samsung\smdk2440\lowlevel_init.S,第134行
.long 0x22011110 //BWSCON .long 0x00000700 //BANKCON0 .long 0x00000700 //BANKCON1 .long 0x00000700 //BANKCON2 .long 0x00000700 //BANKCON3 .long 0x00000700 //BANKCON4 .long 0x00000700 //BANKCON5 .long 0x00018005 //BANKCON6 .long 0x00018005 //BANKCON7 .long 0x008C04F4 // REFRESH .long 0x000000B1 //BANKSIZE .long 0x00000030 //MRSRB6 .long 0x00000030 //MRSRB7
然后是NAND FLASH,还是使用韦东山老师之前就写好的init.c文件,这个文件里面已经写好了NAND FLASH初始化,代码复制到SDRAM,以及打印调试信息等函数,大家直接调用就好了。然后为了跟u-boot自带的函数名字冲突,我在需要外部调用的函数后面全部都加上了“_zyl”以示区分,然后只在本文件调用的都加上了static关键字。这个init.c文件我会放到文末的链接里,大家下载后复制到 \board\samsung\smdk2440目录下,接着修改该目录下的Makefile文件就可以直接调用里面的函数。
\board\samsung\smdk2440\Makefile,第8行
obj-y := smdk2440.o obj-y += lowlevel_init.o init.o
又因为我们只有4K的空间,要把init.c,vectors.S,start.S,crt0.S文件放到程序的最开头,而在这个版本,u-boot会把一个文件夹下的文件一起编译到一个built-in.o文件,我们看一下crt0.S所在的**\arch\arm\lib**文件夹下有好多文件,所以修改一下Makefile文件,把“obj-y”改成“extra-j”,意思就是单独编译vectors.S和crt0.S两个文件。
\arch\arm\lib\Makefile,第12行
ifdef CONFIG_CPU_V7M obj-y += vectors_m.o crt0.o else ifdef CONFIG_ARM64 obj-y += crt0_64.o else # obj-y += vectors.o crt0.o extra-y += vectors.o crt0.o endif
接着修改链接脚本把几个文件放到开头,保证在4K的范围里面
\arch\arm\cpu\u-boot.lds,第41行
. = ALIGN(4); .text : { *(.__image_copy_start) *(.vectors) CPUDIR/start.o (.text*) arch/arm/lib/crt0.o (.text*) arch/arm/lib/vectors.o (.text*) board/samsung/smdk2440/built-in.o (.text*) *(.text*)
最后直接用“bl”指令就可以直接调用已经写好的NAND FLASH函数
\arch\arm\lib\crt0.S,第73行
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK) ldr sp, =(CONFIG_SPL_STACK) #else ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) #endif #if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */ mov r3, sp bic r3, r3, #7 mov sp, r3 #else bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ #endif bl nand_init_zyl
复制代码以及清除BSS段最后跳转执行
\arch\arm\lib\crt0.S,第87行
ldr r0, =0 ldr r1, =_start ldr r2, =__bss_start sub r2, r2, r1 bl copy_code_to_sdram_zyl bl clear_bss_zyl ldr pc, =START_ON_RAM //start run on RAM START_ON_RAM: mov r0, sp bl board_init_f_alloc_reserve mov sp, r0 /* set up gd here, outside any C code */ mov r9, r0 bl board_init_f_init_reserve mov r0, #0 bl board_init_f
在汇编中要调用C函数就可以直接用bl指令,当函数需要输入参数的时候,r0,r1,r2…分别对应输入的第一个参数,第二个参数,第三个参数…。所以上面的代码就是指定了复制代码的源,目的,复制的长度。
然后再调用已经做好的清除BSS段的函数。
最后使用长跳转指令,在代码上看好像只是接着让程序执行下一条指令,但是实际上这个时候是程序从片内跳转到片外SDRAM接着往下执行。
去除原来代码中的重定位和清除BSS段代码
\arch\arm\lib\crt0.S,第132行
#if defined(CONFIG_CPU_V7M) orr lr, #1 /* As required by Thumb-only */ #endif ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */ //b relocate_code ldr r2, =__rel_dyn_start /* r2 <- SRC &__rel_dyn_start */ ldr r3, =__rel_dyn_end /* r3 <- SRC &__rel_dyn_end */ here: /* * now relocate vectors */ // bl relocate_vectors /* Set up final (full) environment */ // bl c_runtime_cpu_setup /* we still call old routine here */ #endif #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK) # ifdef CONFIG_SPL_BUILD /* Use a DRAM stack for the rest of SPL, if requested */ bl spl_relocate_stack_gd cmp r0, #0 movne sp, r0 movne r9, r0 # endif #if 0 ldr r0, =__bss_start /* this is auto-relocated! */ #ifdef CONFIG_USE_ARCH_MEMSET ldr r3, =__bss_end /* this is auto-relocated! */ mov r1, #0x00000000 /* prepare zero to clear BSS */ subs r2, r3, r0 /* r2 = memset len */ bl memset #else ldr r1, =__bss_end /* this is auto-relocated! */ m 1cca7 ov r2, #0x00000000 /* prepare zero to clear BSS */ clbss_l:cmp r0, r1 /* while not at end of BSS */ #if defined(CONFIG_CPU_V7M) itt lo #endif strlo r2, [r0] /* clear 32-bit BSS word */ addlo r0, r0, #4 /* move to next */ blo clbss_l #endif #endif #if ! defined(CONFIG_SPL_BUILD) bl coloured_LED_init bl red_led_on #endif /* 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 */
辅助调试
最后简单介绍一下怎么用init.c中的打印调试信息。这边直接通过一个例子讲解:
bl uart0_init_zyl //初始化串口 ldr r0, =72 //装载要打印的字符,字符按ASCII码值来算,比如我现在要打印'H'对应的值就是72 bl putc_for_test //打印字符‘H’ ldr r0, =73 // ‘I’ bl putc_for_test //打印字符‘I’
关于这节的代码就都移植完毕了,然后分别通过以下几条指令,把u-boot烧到nand flash里面
usb 1 30000000 nand erase 0 80000 nand write 30000000 0 80000
最后实验结果如图所示:
疑问
\arch\arm\lib\crt0.S,第137行
ldr r2, =__rel_dyn_start /* r2 <- SRC &__rel_dyn_start */ ldr r3, =__rel_dyn_end /* r3 <- SRC &__rel_dyn_end */
如果程序中没有这两行,连串口数据都没有会直接死机,但是这两句话我明明是放在串口初始化后面,正常来说如果是这两句造成的影响,那么少了这两句话应该是串口初始化完毕,打印出数据再死机。
然后我用工具把有没有这两句语句的bin文件分别转化为反汇编,再比较,发现程序的所有地址都不一样了。这个BUG真的有点奇怪,不知道有没有大神解答一下,谢谢。
补丁yizhi_3.patch以及init.c
链接:https://pan.baidu.com/s/1v85_5VApVgmMG9IXrwgCiQ
提取码:bb6y
注意:这边的补丁文件只是相对于上一节的u-boot,直接下载下来的u-boot打这个补丁是没有用的。
阅读更多- 移植U-BOOT-2016.11到JZ2440详细教程(5)
- 移植U-BOOT-2016.11到JZ2440详细教程(6)
- 移植U-BOOT-2016.11到JZ2440详细教程(8)
- 移植U-BOOT-2016.11到JZ2440详细教程(7)
- JZ2440平台移植uboot 2016.11(一)
- JZ2440平台移植uboot 2016.11(六)
- JZ2440平台移植uboot 2016.11(三)
- JZ2440平台移植uboot 2016.11(四)
- JZ2440平台移植uboot 2016.11(五)
- JZ2440平台移植uboot 2016.11(二)
- JZ2440平台移植uboot 2016.11(十一)
- JZ2440平台移植uboot 2016.11(八)
- S5pv210 u-boot-2010.03移植详细教程
- u-boot-2016.11移植到S3C2440之新建一个单板(1)
- 移植QT5.6到嵌入式开发板(史上最详细的QT移植教程)
- Springboot SpringCloud集成OAuth2入门详细教程
- 将 TensorFlow 移植到 Android手机,实现物体识别、行人检测和图像风格迁移详细教程
- ARM QT移植详细步骤教程
- U- BOOT源码分析及移植(非常详细)
- spring boot 用war包部署到tomcat下详细教程(解决缺少web.xml报错的问题)