NAND Flash启动的问题

 

从Nand Flash启动U-BOOT的基本原理
-------------------------------------------
前4K的问题
    如果S3C2410被配置成从Nand Flash启动(
配置由硬件工程师在电路板设置), S3C2410的Nand Flash控制器有一个特殊的功能,在S3C2410上电后,Nand Flash控制器会自动的把Nand Flash上的前4K数据搬移到4K内部SRAM,并把0x00000000设置内部SRAM的起始地址,CPU从内部SRAM的0x00000000位置开始启动。这个过程不需要程序干涉
    程序员需要完成的工作,是把最核心的启动程序放在Nand Flash的前4K中。
启动程序的安排
    由于Nand Flash控制器从Nand Flash中搬移到内部SRAM的代码是有限的,所以在启动代码的前4K里,我们必须完成S3C2410的核心配置以及把启动代码(U-BOOT)剩余部分搬到SDRAM中运行。


    u-boot源码不支持从nand flash启动,可是s3c2410支持从nand flash启动,开发板(sbc-2410x)加电后s3c2410将nand flash的前4k(保存有u-boot的部分功能--拷贝功能--把nand flash中的内容拷贝到SDRAM)拷贝到sram(s3c2410芯片内的sram)。这就需要修改u-boot源码,增加u-boot的功能: 使u-boot在得到执行权后能够将其自身拷贝到开发板上SDRAM中,以便处理器能够执行u-boot。

* NOR FLASH地址线和数据线分开,来了地址和控制信号,数据就出来。

*NAND Flash地址线和数据线在一起,需要用程序来控制,才能出数据。通俗的说,就是光给地址不行,要先命令,再给地址,才能读到NAND的数据。而且都是在一个总线完成的。

 Nand Flash的命令、地址、数据都通过I/O口发送,管脚复用,这样做做的好处是,可以明显减少NAND FLASH的管脚数目,将来如果设计者想将NAND FLASH更换为更高密度、更大容量的,也不必改动电路板。

 NAND FLASH不能够执行程序,本人总结其原因如下 :

1. NAND FLASH本身是连接到了控制器上而不是系统总线上。CPU启动后是要取指令执行的,如果是SROM、NOR FLASH 等之类的,CPU 发个地址就可以取得指令并执行,NAND FLASH不行,因为NAND FLASH 是管脚复用,它有自己的一套时序,这样CPU无法取得可以执行的代码,也就不能初始化系统了。

 2. NAND FLASH是顺序存取设备,不能够被随机访问,程序就不能够分支或跳转,这样你如何去设计程序。

 

U-BOOT 支持ARM、 PowerPC等多种架构的处理器,也支持Linux、NetBSD和VxWorks等多种操作系统,主要用来开发嵌入式系统初始化代码 bootloader。bootloader是芯片复位后进入操作系统之前执行的一段代码,完成由硬件启动到操作系统启动的过渡,为运行操作系统提供基本的运行环境,如初始化CPU、堆栈、初始化存储器系统等,其功能类似于PC机的BIOS.



NAND闪存工作原理

       S3C2410开发板的NAND闪存由NAND闪存控制器(集成在S3C2410 CPU中)和NAND闪存芯片(K9F1208U0A)两大部分组成。当要访问NAND闪存芯片中的数据时,必须通过NAND闪存控制器发送命令才能完成。所以, NAND闪存相当于S3C2410的一个外设,而不位于它的内存地址区。

       NAND闪存(K9F1208U0A)的数据存储结构分层为:1设备(Device) = 4096 块(Block);1块= 32页/行(Page/row);1页= 528B = 数据块 (512B) + OOB块 (16B)
在每一页中,最后16个字节(又称OOB)在NAND闪存命令执行完毕后设置状态,剩余512个字节又分为前半部分和后半部分。可以通过NAND闪存命令00h/01h/50h分别对前半部、后半部、OOB进行定位,通过NAND闪存内置的指针指向各自的首地址。
NAND闪存的操作特点为:擦除操作的最小单位是块;NAND闪存芯片每一位只能从1变为0,而不能从0变为1,所以在对其进行写入操作之前一定要将相应块擦除;OOB部分的第6字节为坏快标志,即如果不是坏块该值为FF,否则为坏块;除OOB第6字节外,通常用OOB的前3个字节存放NAND闪存的硬件 ECC(校验寄存器)码;
   
从NAND闪存启动U-BOOT的设计思路

       如果S3C2410被配置成从NAND闪存启动,上电后,S3C2410的NAND闪存控制器会自动把NAND闪存中的前4K数据搬移到内部RAM中, 并把0x00000000设置为内部RAM的起始地址, CPU从内部RAM的0x00000000位置开始启动。因此要把最核心的启动程序放在NAND闪存的前4K中。

       由于NAND闪存控制器从NAND闪存中搬移到内部RAM的代码是有限的,所以, 在启动代码的前4K里,必须完成S3C2410的核心配置,并把启动代码的剩余部分搬到RAM中运行。在U-BOOT中, 前4K完成的主要工作就是U-BOOT启动的第一个阶段(stage1)。
根据U-BOOT的执行流程图,可知要实现从NAND闪存中启动U-BOOT,首先需要初始化NAND闪存,并从NAND闪存中把U-BOOT搬移到RAM中,最后需要让U-BOOT支持NAND闪存的命令操作。
##################################################################################

 

对u-boot启动代码start.s(汇编部分)的注解。

需要注意的是代码的搬运过程。

对于从nandflash启动的s3c2410,上电后会自动从nandflash搬移4k代码(肯定包含start.s部分)到0地址的缓存(bootstone)中,然后从0地址开始读取指令并运行。那么这4k的代码必须实现必要的初始化和u-boot代码的搬移(到sdram中),最后u-boot将在sdram中运行。

u-boot默认是从norflash或sdram中启动运行的。原版的start.s对于代码搬移的实现如下:

1,首先读取_start的地址值(相对pc的值,特别注意adr伪指令)和_TEXT_BASE地址处TEXT_BASE的值(在连接脚本中定义的常数),以判别程序从哪里运行,是否需要搬移。

relocate:               /* relocate U-Boot to RAM       */

    adr r0, _start      /* r0 <- current position of code   */

    ldr r1, _TEXT_BASE      /* test if we run from flash or RAM */

    cmp     r0, r1                

    beq     stack_setup

 

2,如果需要,搬移所有整个代码段到sdram(TEXT_BASE地址处)。下面用于计算代码段长度和代码段终止地址。

 

    ldr r2, _armboot_start

    ldr r3, _bss_start

    sub r2, r3, r2      /* r2 <- size of armboot            */

    add r2, r0, r2      /* r2 <- source end address         */

 

3,如果是从nandflash启动,上述实际上只有效搬移了4k的代码到sdram,因为u-boot代码段大部分还在nandflash中。所以需要添加从nandflash拷贝到sdram的代码。这里由copy_myself函数实现,再次搬移了128k,并且覆盖了前次搬移的4k。

 

     bl    copy_myself

 

4,搬移完成后,程序必须跳到sdram中去运行。注意下面的ldr伪指令,得到on_the_ram的绝对地址(连接完成后就是个常数值,位于sdram中),然后赋值给pc。

 

    @ jump to ram

      ldr   r1, =on_the_ram

      add   pc, r1, #0

 

5,跳到start_armboot(),运行第一跳c指令。这里的ldr是普通指令,_start_armboot标号处存储了start_armboot()函数的绝对地址(连接完就是个常数)。到此start.s的工作完成。

    on_the_ram:

    #endif

    /*end add*/

 

        ldr pc, _start_armboot

 

    _start_armboot: .word start_armboot

 

 
##################################################################################
# cd u-boot-1.1.6

在board/bks2410加入NAND Flash读函数,建立nand_read.c,加入如下内容(copy from vivi):
# vi board/sbc2410x/
nand_read.c
-----------------------------------------------
#include <config.h>
#define __REGb(x) (*(volatile unsigned char *)(x))
#define __REGi(x) (*(volatile unsigned int *)(x))
#define NF_BASE   0x4e000000
#define NFCONF    __REGi(NF_BASE + 0x0)
#define NFCMD     __REGb(NF_BASE + 0x4)
#define NFADDR    __REGb(NF_BASE + 0x8)
#define NFDATA    __REGb(NF_BASE + 0xc)
#define NFSTAT    __REGb(NF_BASE + 0x10)

#define BUSY 1
inline void wait_idle(void) {
    int i;

    while(!(NFSTAT & BUSY))
        for(i=0; i<10; i++);
}

#define NAND_SECTOR_SIZE 512
#define NAND_BLOCK_MASK   (NAND_SECTOR_SIZE - 1)

/* low level nand read function */
int nand_read_ll(unsigned char *buf,
                unsigned long start_addr,
                int            size)
{
    int i, j;

    if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) {
        return -1; /* invalid alignment */
    }

    /* chip Enable */
    NFCONF &= ~0x800;
    for(i=0; i<10; i++);

    for(i=start_addr; i < (start_addr + size);) {
        /* READ0 */
        NFCMD = 0;

        /* Write Address */
        NFADDR = i & 0xff;
        NFADDR = (i >> 9) & 0xff;
        NFADDR = (i >> 17) & 0xff;
        NFADDR = (i >> 25) & 0xff;

        wait_idle();

        for(j=0; j < NAND_SECTOR_SIZE; j++, i++) {
            *buf = (NFDATA & 0xff);
            buf++;
        }
    }

    /* chip Disable */
    NFCONF |= 0x800; /* chip disable */

    return 0;
}



# vi board/sbc2410x/Makefile +28
-----------------------------------------------
COBJS   := sbc2410x.o flash.o        ==>
COBJS   := sbc2410x.o flash.o nand_read.o




# vi cpu/arm920t/start.S
-----------------------------------------------
从Nand Flash中把数据拷贝到RAM,是由copy_myself程序段完成
(1) 在"ldr pc, _start_armboot"之前加入(223行):
#ifdef CONFIG_S3C2410_NAND_BOOT
   bl     copy_myself

   @ jump to ram
   ldr    r1, =on_the_ram
   add    pc, r1, #0
   nop
   nop
   1:     b      1b           @ infinite loop

on_the_ram:
#endif


(2) 在"_start_armboot: .word start_armboot"之后加入:
#ifdef CONFIG_S3C2410_NAND_BOOT
copy_myself:
   mov r10, lr
@ reset NAND
   mov r1, #NAND_CTL_BASE
   ldr r2, =0xf830              @ initial value
   str r2, [r1, #oNFCONF]
   ldr r2, [r1, #oNFCONF]
   bic r2, r2, #0x800           @ enable chip
   str r2, [r1, #oNFCONF]
   mov r2, #0xff                @ RESET command
   strb r2, [r1, #oNFCMD]
   mov r3, #0                   @ wait

1:add   r3, r3, #0x1
   cmp r3, #0xa
   blt 1b
2:ldr   r2, [r1, #oNFSTAT]       @ wait ready
   tst r2, #0x1
   beq 2b
   ldr r2, [r1, #oNFCONF]
   orr r2, r2, #0x800          @ disable chip
   str r2, [r1, #oNFCONF]

@ get read to call C functions (for nand_read())
   ldr sp, DW_STACK_START       @ setup stack pointer
   mov fp, #0                   @ no previous frame, so fp=0

@ copy vivi to RAM
   ldr    r0, =UBOOT_RAM_BASE
   mov    r1, #0x0
   mov    r2, #0x20000
   bl     nand_read_ll
   tst    r0, #0x0
   beq    ok_nand_read

#ifdef CONFIG_DEBUG_LL
   bad_nand_read:
   ldr    r0, STR_FAIL
   ldr    r1, SerBase
   bl     PrintWord
1:b       1b           @ infinite loop
   #endif

ok_nand_read:
#ifdef CONFIG_DEBUG_LL
   ldr    r0, STR_OK
   ldr    r1, SerBase
   bl     PrintWord
#endif

@ verify
   mov    r0, #0
   ldr    r1, =UBOOT_RAM_BASE
   mov    r2, #0x400      @ 4 bytes * 1024 = 4K-bytes
go_next:
   ldr    r3, [r0], #4
   ldr    r4, [r1], #4
   teq    r3, r4
   bne    notmatch
   subs   r2, r2, #4
   beq    done_nand_read
   bne    go_next

notmatch:
#ifdef CONFIG_DEBUG_LL
   sub    r0, r0, #4
   ldr    r1, SerBase
   bl     PrintHexWord
   ldr    r0, STR_FAIL
   ldr    r1, SerBase
   bl     PrintWord
#endif
1:b       1b
done_nand_read:
#ifdef CONFIG_DEBUG_LL
   ldr    r0, STR_OK
   ldr    r1, SerBase
   bl     PrintWord
#endif
   mov    pc, r10
@ clear memory
@ r0: start address
@ r1: length
   mem_clear:
   mov r2, #0
   mov r3, r2
   mov r4, r2
   mov r5, r2
   mov r6, r2
   mov r7, r2
   mov r8, r2
   mov r9, r2

clear_loop:
   stmia r0!, {r2-r9}
   subs   r1, r1, #(8 * 4)
   bne    clear_loop
   mov    pc, lr

#endif @ CONFIG_S3C2410_NAND_BOOT


(3) 在文件的最后加入:
    .align      2
DW_STACK_START:
.word       STACK_BASE+STACK_SIZE-4





# vi include/configs/sbc2410x.h
-----------------------------------------------
(1) 将CONFIG_SERVERIP设置为主机IP
    这样以后在通过网络下载内核映像时,就不用就该IP地址了。(这一项修改与nand flash没有关系)

#define CONFIG_SERVERIP     192.168.0.240


(2) 在文件末尾添加如下内容
/*
* Nandflash Boot
*/
#define CONFIG_S3C2410_NAND_BOOT 1
#define STACK_BASE         0x33f00000
#define STACK_SIZE         0x8000
#define UBOOT_RAM_BASE     0x33f80000
/* NAND Flash Controller */
#define NAND_CTL_BASE      0x4E000000
#define bINT_CTL(Nb)       __REG(INT_CTL_BASE + (Nb))
/* Offset */
#define oNFCONF            0x00
#define oNFCMD             0x04
#define oNFADDR            0x08
#define oNFDATA            0x0c
#define oNFSTAT            0x10
#define oNFECC             0x14

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值