NAND Flash启动的问题
从Nand Flash启动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,#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,#oNFCONF] ?? bic r2,r2,#0x800?????????? @ enable chip ?? str r2,#oNFCONF] ?? mov r2,#0xff??????????????? @ RESET command ?? strb r2,#oNFCMD] ?? mov r3,#0?????????????????? @ wait 1:add?? r3,#0x1 ?? cmp r3,#0xa ?? blt 1b 2:ldr?? r2,#oNFSTAT]?????? @ wait ready ?? tst r2,#0x1 ?? beq 2b ?? ldr r2,#oNFCONF] ?? orr r2,#0x800????????? @ disable chip ?? str r2,#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,#4 ?? beq??? done_nand_read ?? bne??? go_next notmatch: #ifdef CONFIG_DEBUG_LL ?? sub??? r0,#4 ?? ldr??? r1,SerBase ?? bl???? PrintHexWord ?? ldr??? r0,SerBase ?? bl???? PrintWord #endif 1:b?????? 1b done_nand_read: #ifdef CONFIG_DEBUG_LL ?? ldr??? r0,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,#(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
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |