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

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位,按照上述修改后,问题解决!

原文地址:https://www.cnblogs.com/jasontian996/p/11388056.html

时间: 2024-10-16 17:56:52

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

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

系统:Centos 6.4 64位: FreeSWITCH版本:1.5 具体的安装过程参考FreeSWITCH 官网wiki (也可以参考我的博客<Centos6安装FreeSWITCH>) 从FreeSWITCH 安装过程./configure 时遇到sqlite 的问题开始: checking for sqlite3 >= 3.6.20… Package sqlite3 was not found in the pkg-config search path. Perhaps you

VMware ESXi 启动时提示引导错误:不是VMware引导槽。找不到管理程序(bank6 not a vmware boot bank no hypervisor found)

VMware ESXi 启动时提示引导错误: bank6 not a vmware boot bank no hypervisor found 大概中文意思是:不是VMware引导槽.找不到管理程序. 网上看到https://communities.vmware.com/thread/429698 有网友解答说,删除分区的字符会导致GPT修改(大意) 才想起重启前,在PE下转移硬盘数据,因为ESXi的分区比较多,看着碍眼手贱删掉了2个分局的字符.... 再看https://kb.vmware.c

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

开启Secure boot情况下,在Fedora 21下安装Nvidia 显卡驱动的方法. Nvidia显卡驱动可以从官网上下载最新版>> 点击进入 下载后添加可执行权限: #chmod +x NVIDIA-Linux*.run 注意,安装Nvidia显卡需要满足的两个条件是 1. nouveau(默认的显卡驱动)驱动程序必须禁用 2. Xserver(图形界面) 要停止运行 以上两个问题的解决方法如下: 首先按Ctrl + Alt + F2进入终端 输入root的帐号和密码后执行以下操作 1

spring boot tomcat 打本地包成war,通过Tomcat启动时出现问题: ZipException: error in opening zip file

一个第三方公司提供spring boot 项目,直接启动是ok的, 但是打包成war,通过Tomcat启动,就出现 ZipException: error in opening zip file: 20-Mar-2019 15:48:28.385 信息 [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive [F:\dev\apache-t

记一次spring boot项目启动时的依赖循环

奇怪的是在我Ubuntu的机器上打包到测试服务器上报错,而从另外一台windows机器打包时就没问题,刚开始还以为是maven和jdk的问题.报错的启动时日志如下: 2019-06-14 14:03:45,644 org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter [main] (LoggingFailureAnalysisReporter.java:38) DEBUG --> Application faile

【问题解决:信息提示】SpringBoot启动时提示The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path

问题描述 springboot程序在启动时提示信息 [2018-10-24 21:59:05.214] - 440 信息 [restartedMain] --- org.apache.catalina.core.StandardService: Starting service [Tomcat] [2018-10-24 21:59:05.220] - 440 信息 [restartedMain] --- org.apache.catalina.core.StandardEngine: Start

Spring Boot启动时出现WARN:No MyBatis mapper was found in

今天发现spring-boot继承mybatis启动时老是出现WARN: org.mybatis.spring.mapper.ClassPathMapperScanner - No MyBatis mapper was found in '[com.aaa.bbb]' package. Please check your configuration. 使用的tk的开源项目进行mybatis集成 查了好多解决方案但是没生效,最后在一篇文章中看到: doScan()会扫描启动类同级目录下的mappe

优化pxe网启动时tftp的传输速度 --- 针对pxelinux和bootmgr

作为一名IT人士,一般的计算机维护当然不好意思找别人. 于是自己用pxelinux搭了个网络启动环境,可以启动各种WinPE,以供折腾电脑系统,刷新固件的需要. 只是一般的网络启动都是基于tftp协议的,传输文件那叫一个慢.启动时光是加载映像文件就得几分钟,简直就像活在史前一样. 找了一圈方法,没辙. 好在这东西也就救急时用用,凑活凑活也行. 于是就这么凑错了几年... 最近升级到win8了,为了维护环境,加了几个win7,win8的PE. winxp的pe就在启动菜单上无比折腾(要到二进制文件

Linux系统开机启动时的工作原理

Linux系统开机启动时的工作原理也是深入了解Linux系统核心工作原理的一个很好的途径. 启动第一步--加载BIOS 当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的重要,以至于计算机必须在最开始就找到它.这是因为BIOS中包含了CPU的相关信息.设备启动顺序信息.硬盘信息.内存信息.时钟信息.PnP特性等等.在此之后,计算机心里就有谱了,知道应该去读取哪个硬件设备了.在BIOS将系统的控制权交给硬盘第一个扇区之后,就开始由Linux来控制系统了. 启动第二步--读取MB