4. 移植u-boot-2016.03之修改代码支持NAND启动
????对于Jz2440开发板,它不仅支持NOR Flash启动,还支持从NAND Flash启动。那么NOR Flash启动与NAND Flash启动有什么区别呢?NOR 启动,上电后NOR Flash被映射到0x00000000地址,然后CPU从0x00000000开始运行;NAND启动,上电后CPU自动将NAND Flash中的前4K代码拷贝到SRAM,SRAM被映射为0x00000000地址,CPU从0x00000000开始运行。uboot的大小远远大于4k,因此需要在代码的前4k中完成代码的重定位,即把NAND Flash的代码拷贝到SDRAM中。关于代码的重定位知识,可以看韦东山老师的博客S3c2440代码重定位详解。
4.1 去"-pie"、"checkarmreloc"选项
????原来的代码在链接时加了”-pie”选项,使得u-boot.bin里多了*(.rel*)段,程序非常大,不利于从NAND启动(重定位之前的启动代码应该少于4K),因此我们需要去掉"-pie"选项。 (1) 在u-boot 的顶层目录中搜索"pie":grep "\-pie" * -nR,搜索结果如下:
从上图可以发现,"pie"选项在arch/arm/config.mk的第83行,那么我们进去把它注释掉,如下图所示:
然后执行如下命令重新编译:
make distclean
make jz2440_defconfig
make
最后发现如下错误:
checkarmreloc正如其名,它是检查arm相对位置的选项,可能是没找到arm,所以出错,对程序整体没有影响,注释掉就不检查了,编译能通过;在uboot顶层目录搜索checkarmreloc:grep "checkarmreloc" * -nR,搜索结果如下:
从上图可以发现,"checkarmreloc"选项在arch/arm/config.mk的第105行,那么我们进去把它注释掉,如下图所示:
然后重新编译就成功编译通过了。
4.2 添加init.c程序
(1) 把之前编写好的init.c文件放到board/samsung/jz2440目录,init.c文件的代码如下:
/*那么如何判断是NOR启动还是NAND启动呢? 可以利用NOR的特点:
*1.NOR可以像内存一样读,但不可以像内存一样写;
*2.可以通过向0地址写数据已区分是NAND启动还是NOR启动;
*(因为NAND启动时,0地址是RAM,可读可写;但NOR启动时,0地址是NOR FLASH,可读不可写)
*返回值:1:Nor启动; 0:NAND启动
*/
#define NFCONF (*((volatile unsigned long *)0x4E000000))
#define NFCONT (*((volatile unsigned long *)0x4E000004))
#define NFCMMD (*((volatile unsigned char *)0x4E000008))
#define NFADDR (*((volatile unsigned char *)0x4E00000c))
#define NFDATA (*((volatile unsigned char *)0x4E000010))
#define NFSTAT (*((volatile unsigned long *)0x4E000020))
/* GPIO */
#define GPHCON (*(volatile unsigned long *)0x56000070)
#define GPHUP (*(volatile unsigned long *)0x56000078)
/* UART registers*/
#define ULCON0 (*(volatile unsigned long *)0x50000000)
#define UCON0 (*(volatile unsigned long *)0x50000004)
#define UFCON0 (*(volatile unsigned long *)0x50000008)
#define UMCON0 (*(volatile unsigned long *)0x5000000c)
#define UTRSTAT0 (*(volatile unsigned long *)0x50000010)
#define UTXH0 (*(volatile unsigned char *)0x50000020)
#define URXH0 (*(volatile unsigned char *)0x50000024)
#define UBRDIV0 (*(volatile unsigned long *)0x50000028)
#define TXD0READY (1<<2)
/* NAND flash 设置时序(需要对比s3c2440与NAND手册的NAND时序)
*
*/
void nand_init_ll(void)
{
/*设置NANDFlash时序*/
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0
NFCONF &= ~((3<<12)|(7<<8)|(7<<4));
NFCONF |= (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
/*使能NANDflash 控制器,
*禁止片选(开发板没正式使用片选,避免错误的操作)
*初始化 ECC 编码器/译码器*/
NFCONT = (1<<0)|(1<<1)|(1<<4);
}
static void nand_cmd(unsigned char cmd)
{
volatile int i;
NFCMMD = cmd;
for(i = 0; i < 10; i++);/*等待*/
}
static void nand_addr(unsigned int addr)
{
unsigned int col = addr % 2048;
unsigned int page = addr / 2048;
volatile int i;
NFADDR = col & 0xff;
for(i = 0; i < 10; i++);/*等待*/
NFADDR = (col >> 8) & 0xff;
for(i = 0; i < 10; i++);/*等待*/
NFADDR = page & 0xff;
for(i = 0; i < 10; i++);/*等待*/
NFADDR = (page >> 8) & 0xff;
for(i = 0; i < 10; i++);/*等待*/
NFADDR = (page >> 16) & 0xff;
for(i = 0; i < 10; i++);/*等待*/
}
static void nand_wait_ready(void)
{
while(!(NFSTAT & 0x01));
}
static unsigned char nand_data(void)
{
return NFDATA;
}
static void nand_select(void)
{
NFCONT &= ~(1<<1);
}
static void nand_deselect(void)
{
NFCONT |= (1<<1);
}
void nand_read_ll(unsigned int addr, unsigned char *buf,unsigned int len)
{
int col = addr % 2048;
int i = 0;
/*1.片选*/
nand_select();
while(i < len)
{
/*2.发出读命令00h*/
nand_cmd(0x00);
/*3.发出地址(分5个地址周期)*/
nand_addr(addr);
/*4.发出读命令30h*/
nand_cmd(0x30);
/*5.等待就绪(判断状态)*/
nand_wait_ready();
/*6.读数据*/
for(; (col < 2048) && (i < len); col++)
{
buf[i] = nand_data();
i++;
addr++;
}
col = 0;
}
/*7.取消选中*/
nand_deselect();
}
static int isBootFromNorFlash(void)
{
volatile unsigned int *p = (volatile unsigned int*)0;
unsigned int val;
val = *p;
*p = 0x12345678;
if(*p == val) /*写前写后值一样,证明无法向0地址写数据,是NOR启动*/
{
return 1;
}else /*否则是NAND启动*/
{
*p = val; /*恢复数据*/
return 0;
}
}
void copy_code_to_sdram(unsigned char *src, unsigned char *dest,unsigned int len)
{
unsigned int i=0;
/*如果是NOR启动*/
if(isBootFromNorFlash()) /*那么如何判断是NOR启动还是NAND启动呢? */
{
while(i < len)
{
dest[i] = src[i];
i++;
}
}else /*否则是NAND启动*/
{
nand_init_ll();/*初始化NAND*/
nand_read_ll((unsigned int )src,dest,len);
}
}
/*c语言如何引用连接脚本的 __bss_start、__bss_end等变量呢?
* extern int __bss_start,__bss_end;
* int *p = &__bss_start;
*
*/
void clean_bss(void)
{
extern int __bss_start,__bss_end;
int *p = &__bss_start;
for(;p < &__bss_end;p++)
{
*p = 0;
}
}
(2) 改board/samsung/jz2440目录下的Makefile,把init.c 文件编译进uboot:
4.3 修改uboot启动流程
(1) 从前面的u-boot-2012.04.01 移植笔记的第七节修改代码支持NAND启动可知,添加重定位函数是在调用boardinitf函数进行第一阶段初始化之前,并且在执行重定位函数之前要设置栈,如下图所示:
所以,参照上图,为u-boot-2016.03支持NAND启动添加重定位代码,所以修改arch/arm/lib/crt0.S文件,在调用boardinitf之前重定位,修改成如下:
ENTRY(_main)
/*
* Set up initial C runtime environment and call board_init_f(0).
*/
#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_ll /*初始化NAND Flash*/
mov r0,#0 /*uboot的源地址*/
ldr r1, =(CONFIG_SYS_TEXT_BASE) /*目的地址,即uboot的重定位地址*/
ldr r2, =_start /* 得到uboot的开始地址(链接地址) */
ldr r3, =__bss_start /* 得到bss段开始地址(链接地址) */
sub r2, r3, r2 /* 得到uboot大小 */
bl copy_code_to_sdram /* 拷贝代码到SDRAM,参数1:uboot源地址,参数2:目的地址,参数3:uboot大小 */
#if 0
bl clean_bss /*注释掉,使用原代码清空bss段*/
#endif
ldr pc,=call_board_init_f
/* Set stackpointer in internal RAM to call board_init_f */
call_board_init_f:
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
(2) 注释掉u-boot自身的重定位代码,如下图所示:
4.4 修改链接地址CONFIGSYSTEXT_BASE为0x33f00000
在前面的u-boot重定位函数copycodetosdram中,使用到CONFIGSYSTEXTBASE作为copycodetosdram
函数的目的地址,那么它在哪里被定义呢?它在include/configs/jz2440.h中被定义,如下图所示:
CONFIG
SYSTEXTBASE为0x33f00000,如下图所示:
4.5 修改uboot重定位地址
从之前的u-boot-2016.03启动过程分析可知, 源码是通过在reserveuboot函数中计算得到的重定位地址,这里的链接地址和重定位地址必须一样。所以需要修改reserveuboot函数的代码,,修改后的代码如下图图所示:
4.6 修改链接脚本: 把start.S, init.c, lowlevel.S等文件放在最前面
链接脚本为:arch/arm/cpu/u-boot.lds 最前面有下面一段代码:
修改为:
然后make重新编译,把u-boot.bin烧写到Jz2440开发板的NAND Flash,并设置NAND Flash启动,串口打印的信息如下:
从上图可知,u-boot-2016.03从NAND Flash启动成功。