u-boot从nand 启动时的问题解决记录

问题描述

使用u-boot-1.1.6版本u-boot移植到JZ2440开发板上,当前已经能够从Nor启动,但是不能从Nand正常启动(u-boot大小为95.8k,已经超过的2440从Nand启动时所能拷贝的4k大小),当前代码中只具备从Nor启动时,重定位代码到SDRam的功能,所以需要添加从Nand启动时将u-boot代码重定位到SDRam中,之后跳到SDRam中运行;

针对上述需求中Nand操作部分增加代码如下

/* s3c2440中nand控制器中寄存器的操作地址 */
#define NFCONF (*(volatile unsigned int *)0x4E000000)
#define NFCONT (*(volatile unsigned int *)0x4E000004)
#define NFCMD (*(volatile unsigned int *)0x4E000008)
#define NFADDR (*(volatile unsigned int *)0x4E00000C)
#define NFDATA (*(volatile unsigned int *)0x4E000010)
#define NFSTAT (*(volatile unsigned int *)0x4E000020) /* 判断当前程序是从Nor启动,还是从Nand启动
* 返回值:0 从nand启动,1 从Nor启动
*/
unsigned int isFromNorflashStart(void)
{
unsigned int val;
volatile unsigned int *p = (volatile unsigned int *)0; val = *p;
*p = 0x12345678;
if(*p == 0x12345678)
{
*p = val;
return 0;
}
else
{
return 1;
}
} /* nand控制器初始化 */
void nand_init_ll(void)
{
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0
/* 设置时序 */
NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
/* 使能Nand控制器、初始化ECC、禁止片选 */
NFCONT = (1<<4) | (1<<1) | (1<<0);
} /* 使能片选 */
void enable_select_ll(void)
{
NFCONT &= ~(1<<1);
} /* 禁止片选 */
void disenable_select_ll(void)
{
NFCONT |= (1<<1);
} /* 发命令 */
void nand_cmd_ll(unsigned char cmd)
{
volatile unsigned int i;
NFCMD = cmd;
for(i=0; i<10; i++);
} /* 发地址 */
void nand_addr_ll(unsigned int addr)
{
volatile unsigned int i;
unsigned int page = addr / 2048;
unsigned int col = addr % 2048; 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++);
} /* 等待就绪 */
void nand_wait_ready_ll(void)
{
while(!(NFSTAT & (1<<0)));
} /* 读数据 */
unsigned char nand_data_ll(void)
{
return NFDATA;
} /* nand读操作 */
void nand_read_ll(unsigned int addr, unsigned char *dest, unsigned int len)
{
unsigned int page = addr / 2048;
unsigned int col = addr % 2048;
unsigned int i = 0; /* 使能片选 */
enable_select_ll(); while(i < len)
{
/* 发出命令00H */
nand_cmd_ll(0x00);
/* 发出地址 */
nand_addr_ll(addr);
/* 发出命令30H */
nand_cmd_ll(0x30);
/* 等待就绪 */
nand_wait_ready_ll();
/* 读数据 */
for(; (col < 2048) && (i < len); col++)
{
dest[i++] = nand_data_ll();
addr++;
}
/* 当读取一页后,将列值置为0 */
col = 0;
}
/* 禁止片选 */
disenable_select_ll();
} /*
* 重定位函数
* addr : 源地址
* dest : 目的地址
* len : 需要拷贝数据的长度
*/
void copy_code_to_sdram(unsigned char *addr, unsigned char *dest, unsigned int len)
{
unsigned int i = 0; if(isFromNorflashStart())
{
while(i < len)
{
*dest++ = *addr++;
i++;
}
}
else
{
nand_read_ll(addr, dest, len);
}
}

针对上述需求功能在汇编代码中修改如下

以下是部分代码段,在此之前的汇编代码主要完成的功能是:将CPU设置为SVC模式、关看门狗、关中断以及设置时钟;

	/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT /* CONFIG_SKIP_LOWLEVEL_INIT未定义 */
bl cpu_init_crit /* 主要的功能是关cache、关MMU以及初始化SDRam */
#endif /* Set up the stack */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ /* CONFIG_USE_IRQ未定义 */
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */ bl nand_init_ll /* 上面已经设置好了栈,调用C函数初始化Nand控制器 */ #ifndef CONFIG_SKIP_RELOCATE_UBOOT /* CONFIG_SKIP_RELOCATE_UBOOT未定义 */
relocate: /* relocate U-Boot to SDRAM */
mov r0, #0
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */ bl copy_code_to_sdram /* 调用代码重定位处理函数 */
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */ clear_bss:
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */ clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
ble clbss_l ldr pc, _start_armboot /* 跳转执行第二阶段 */ _start_armboot: .word start_armboot

结果

添加上述代码、编译后得到u-boot.bin烧写到Nand中的0地址处,上电运行,发现在串口监控中没有任何信息输出,之后又将代码烧写到Nor上的0地址处,上电运行,发现串口监控中输出信息正常,命令执行也正常;

分析

  1. u-boot程序的连接地址指定为-Ttext 0x33F80000 ,而当开发板设置为从Nand启动时,CPU会先拷贝4k大小的代码到芯片内部的ram中运行,那么问题来了,有没有可能在连接时,重定位的代码部分并没有在前4k大小中,从而导致代码重定位失败,无法正常启动?

    解决办法:查看u-boot.bin文件反编译得到的u-boot.dis文件中,重定位代码部分的连接地址是否在前4k空间,发现确实在前4k空间(如果不在前4k空间,可以修改连接脚本u-boot.lds文件,确保相关文件优先连接);

    /* 反汇编得到的部分内容 */
    33f80cd8 <copy_code_to_sdram>:
    33f80cd8: e92d4070 stmdb sp!, {r4, r5, r6, lr}
    33f80cdc: e1a04002 mov r4, r2
    33f80ce0: e1a05000 mov r5, r0
    33f80ce4: e1a06001 mov r6, r1
    33f80ce8: ebffff68 bl 33f80a90 <isFromNorflashStart>
    33f80cec: e3500000 cmp r0, #0 ; 0x0
    33f80cf0: e3a02000 mov r2, #0 ; 0x0
    33f80cf4: 0a000007 beq 33f80d18 <copy_code_to_sdram+0x40>
    33f80cf8: e1520004 cmp r2, r4
    33f80cfc: 28bd8070 ldmcsia sp!, {r4, r5, r6, pc}
    33f80d00: e2822001 add r2, r2, #1 ; 0x1
    33f80d04: e4d53001 ldrb r3, [r5], #1
    33f80d08: e1520004 cmp r2, r4
    33f80d0c: e4c63001 strb r3, [r6], #1
    33f80d10: 3afffffa bcc 33f80d00 <copy_code_to_sdram+0x28>
    33f80d14: e8bd8070 ldmia sp!, {r4, r5, r6, pc}
    33f80d18: e1a00005 mov r0, r5
    33f80d1c: e1a01006 mov r1, r6
    33f80d20: e1a02004 mov r2, r4
    33f80d24: e8bd4070 ldmia sp!, {r4, r5, r6, lr}
    33f80d28: eaffffc9 b 33f80c54 <nand_read_ll>
  2. 如果程序连接没有问/题,那么有可能是nand控制器初始化失败,或者是在调用copy_code_to_sdram函数时传入的参数不对导致的?

    nand控制器初始化失败经过再次确认可以排除这种情况;

        mov r0, #0
    ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
    ldr r2, _armboot_start
    ldr r3, _bss_start
    sub r2, r3, r2 /* r2 <- size of armboot */ bl copy_code_to_sdram /* 调用代码重定位处理函数 */

    在汇编代码中调用C函数copy_code_to_sdram时是通过r0、r1、r2分别传入源地址、目的地址和数据长度等三个变量,其中r0 = 0,代码本身就是烧写在Nand的0地址处,所以源地址为0,没问题,r1 = _TEXT_BASE = 0x33F80000 ,连接地址在连接是指定为0x33F80000 ,所以也没问题,r2 = _armboot_start = _start = 代码的起始地址(在连接脚本中该地址被指定为 . = 0x00000000;),r3 = _bss_start是.bss段的起始地址,当r3 - r2后得到的就是代码段的长度,所以也没问题;

  3. 目前最大的嫌疑就是,Nand在拷贝数据的过程中不成功导致的?

    解决办法:重点分析nand_read_ll()函数,确保数据读取成功并且是正确的,此处省略描述这个痛苦的过程,直接上结果,nand_read_ll()函数本身的逻辑处理是没问题的,而问题出在了如下部分:

    /* s3c2440中nand控制器中寄存器的操作地址 */
    #define NFCONF (*(volatile unsigned int *)0x4E000000)
    #define NFCONT (*(volatile unsigned int *)0x4E000004)
    #define NFCMD (*(volatile unsigned int *)0x4E000008)
    #define NFADDR (*(volatile unsigned int *)0x4E00000C)
    #define NFDATA (*(volatile unsigned int *)0x4E000010)
    #define NFSTAT (*(volatile unsigned int *)0x4E000020) 将上面的NFCMD、NFADDR、NFDATA和NFSTAT地址的宏定义修改为:
    #define NFCMD (*(volatile unsigned char *)0x4E000008)
    #define NFADDR (*(volatile unsigned char *)0x4E00000C)
    #define NFDATA (*(volatile unsigned char *)0x4E000010)
    #define NFSTAT (*(volatile unsigned char *)0x4E000020)

    在2440的datasheet中Nand控制器寄存器描述部分,上述修改了的寄存器,只关心低8位,按照上述修改后,问题解决!

u-boot从nand 启动时的问题解决记录的更多相关文章

  1. spring boot 项目在启动时执行指定sql文件

    参考博客: https://www.jianshu.com/p/88125f1cf91c 1. 启动时执行 当有在项目启动时先执行指定的sql语句的需求时,可以在resources文件夹下添加需要执行 ...

  2. SpringBoot 源码解析 (三)----- Spring Boot 精髓:启动时初始化数据

    在我们用 springboot 搭建项目的时候,有时候会碰到在项目启动时初始化一些操作的需求 ,针对这种需求 spring boot为我们提供了以下几种方案供我们选择: ApplicationRunn ...

  3. Spring Boot学习--项目启动时执行指定service的指定方法

    Springboot给我们提供了两种“开机启动”某些方法的方式:ApplicationRunner和CommandLineRunner. 这两种方法提供的目的是为了满足,在项目启动的时候立刻执行某些方 ...

  4. Spring Boot学习--项目启动时执行特定方法

    Springboot给我们提供了两种"开机启动"某些方法的方式:ApplicationRunner和CommandLineRunner. 这两种方法提供的目的是为了满足,在项目启动 ...

  5. Centos6安装FreeSWITCH 1.5时./configure问题解决记录

    系统:Centos 6.4 64位: FreeSWITCH版本:1.5 具体的安装过程参考FreeSWITCH 官网wiki (也可以参考我的博客<Centos6安装FreeSWITCH> ...

  6. 让Spring Boot项目启动时可以根据自定义配置决定初始化哪些Bean

    让Spring Boot项目启动时可以根据自定义配置决定初始化哪些Bean 问题描述 实现思路 思路一 [不符合要求] 思路二[满足要求] 思路三[未试验] 问题描述 目前我工作环境下,后端主要的框架 ...

  7. arm:启动代码判断是从nand启动还是从norflash启动,拷贝程序到内存的过程

    一.nand启动和nor启动:[1] CPU从0x00000000位置开始运行程序. 1.nand启动: 如果将S3C2440配置成从NANDFLASH启动(将开发板的启动开关拔到nand端,此时OM ...

  8. 2.移植uboot-添加2440单板,并实现NOR、NAND启动

    上章分析了uboot启动流程后,接下来便来配置新的单板,实现nor.nand启动 1.首先在uboot里新建单板2440 : cd board/samsung/ cp smdk2410 smdk244 ...

  9. Linux secure boot(安全启动)时添加Nvidia显卡驱动

    开启Secure boot情况下,在Fedora 21下安装Nvidia 显卡驱动的方法. Nvidia显卡驱动可以从官网上下载最新版>> 点击进入 下载后添加可执行权限: #chmod ...

随机推荐

  1. Cannon 60D 电池卡在电池槽了,拔不出来怎么办?

    事情是这样的,本来好好的电池在电池槽里的,后来拿去充电了,充满后就准备装回去,然后一个不小心,电池掉地上了,就看了一下没摔爆,所以也没特别留意有没有什么地方摔坏摔瘸角,然后就往相机里塞,突然就发现塞不 ...

  2. matlab 提取图像轮廓(图像边缘提取)

    利用edge()函数提取图像轮廓,绘制出对象的边界和提取边界坐标信息,matlab实现代码如下: close all;clear all;clc; % 提取图像轮廓,提取图像边缘 I = imread ...

  3. [BAT] cmd 管理员权限 右键菜单 运行

    将下面保存为 *.reg Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\Directory\Background\shell\runa ...

  4. 2019Java常见面试下

    1.集合的作用是什么? 数据的传送增.删.改.查.constainsAll,可以存放不同类型的对象. 2.集合的通用方法有那些?通用方法是什么?(操作) 集合List的遍历方法有: Iterator: ...

  5. Charles系列三:Charles打断点(包含修改请求,修改返回的内容),模拟慢速网络(弱网测试),域名映射,过滤请求,接口调试,打压测试

    一:Charles断点的使用(包含修改请求,修改返回的数据) 设置断点来修改请求和返回的数据,在开发过程中可以模拟多种响应.步骤如下: 1.添加断点方法有两种: 方法1:找到Charles中菜单项Pr ...

  6. 学习笔记:CentOS7学习之十五: RAID磁盘阵列的原理与搭建

    目录 学习笔记:CentOS7学习之十五: RAID磁盘阵列的原理与搭建 14.1 RAID概念 14.1.1 RAID几种常见的类型 14.1.2 RAID-0工作原理 14.1.3 RAID-1工 ...

  7. Windows应急响应常见命令

    ---恢复内容开始--- 1.查看所有连接的PID netstat -ano 2.过滤特定端口 netstat -ano | findstr “443” 3.查看占用443端口的进程 tasklist ...

  8. Memcached和Spring集成开发

    xml配置文件 <bean id="memcachedPool" class="com.danga.MemCached.SockIOPool" facto ...

  9. Spring Boot(一) 初步理解Spring Boot

    一.Spring Boot所解决的问题 Java开发十分笨重:繁多的配置.低下的开发效率.复杂的部署流程以头疼的第三方技术集成. Spring Boot的理念:习惯优于配置——项目中存在大量的配置,此 ...

  10. JS基础_函数的简介

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...