从nandflash启动uboot的基本原理.doc
u-boot_2010.6nandflash驱动彻底分析
u-boot_2010.6nandflash驱动彻底分析2017年11⽉13⽇15:37:34最近公司⼤裁员,闹的⼈⼼惶惶,不管怎么样,武装好⾃⼰才是硬道理,坚持学习,学会那些还没学会的。
今天虚拟机突然打不开了,吓了我⼀跳,因为代码都还没备份,⼀定得养成备份代码的习惯!好了,下⾯开始进⼊正题吧,nandflash驱动彻底分析底层驱动移植完后,执⾏nand 命令:nand read 0x30000000 0 0x2000nand read 的命令格式是 nand read addr off | partition size ,总结起来就是读到哪去?从哪读?读多⼤?返回的结果是:NAND read: device 0 offset 0x0, size 0x2000file is nand_util.c,fun is nand_read_skip_bad,line is 599,NAND read from offset 2000 failed -740 bytes read: ERROR读失败,给出读失败错误码 -74要分析失败原因,先分析函数执⾏流程执⾏nand read 命令后,其实是执⾏了nand_read_skip_bad(nand, off, &size,(u_char *)addr);跳过坏块读函数的参数简单明了,从哪读,读到哪去,读多少,以及⼀个公共句柄(包含nand的信息,例如有多少个块,块⼤⼩等) 1int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,2 u_char *buffer)3 {4int rval;5 size_t left_to_read = *length;6 size_t len_incl_bad;7 u_char *p_buffer = buffer;89 len_incl_bad = get_len_incl_bad (nand, offset, *length); /* 函数分析见2017.11.14笔记(往下翻) */1011if ((offset + len_incl_bad) > nand->size) {12 printf ("Attempt to read outside the flash area\n");13return -EINVAL;14 }1516if (len_incl_bad == *length) {17 rval = nand_read (nand, offset, length, buffer);18if (!rval || rval == -EUCLEAN)19return0;20 printf ("NAND read from offset %llx failed %d\n",21 offset, rval);22return rval;23 }2425while (left_to_read > 0) {26 size_t block_offset = offset & (nand->erasesize - 1);27 size_t read_length;2829 WATCHDOG_RESET ();3031if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {32 printf ("Skipping bad block 0x%08llx\n",33 offset & ~(nand->erasesize - 1));34 offset += nand->erasesize - block_offset;35continue;36 }3738if (left_to_read < (nand->erasesize - block_offset))39 read_length = left_to_read;40else41 read_length = nand->erasesize - block_offset;42/* read_length 最⼤不会超过 nand->erasesize (128K) */43/* nand_read 函数是⼀个按块读函数 */44 rval = nand_read (nand, offset, &read_length, p_buffer);45if (rval && rval != -EUCLEAN) {46 printf ("NAND read from offset %llx failed %d\n",47 offset, rval);48 *length -= left_to_read;49return rval;50 }5152 left_to_read -= read_length;53 offset += read_length;54 p_buffer += read_length;55 }5657return0;58 }先看get_len_incl_bad1static size_t get_len_incl_bad (nand_info_t *nand, loff_t offset,2const size_t length)3 {4 size_t len_incl_bad = 0;5 size_t len_excl_bad = 0;6 size_t block_len;78while (len_excl_bad < length) {9 block_len = nand->erasesize - (offset & (nand->erasesize - 1));1011if (!nand_block_isbad (nand, offset & ~(nand->erasesize - 1)))12 len_excl_bad += block_len;1314 len_incl_bad += block_len;15 offset += block_len;1617if (offset >= nand->size)18break;19 }2021return len_incl_bad;22 }想要看懂这个函数,先要理解offset的构成2017年11⽉14⽇10:00:40每天进步⼀点点nand->erasesize = 128K = 0x20000nand->erasesize - 1 = 0x1FFFFoffset & (nand->erasesize - 1) 保留低17位,清⾼位,因为nand的特性,所以是按块来统计长度block_len = nand->erasesize - offset & (nand->erasesize - 1) 作⽤是统计当前块要读的长度然后判断当前块是不是坏块,如果不是坏块,则让len_excl_bad += block_len,判断是否循环的标准是len_excl_bad < length 不论当前块是不是坏块,都让len_incl_bad += block_len,len_incl_bad 得到的是包含坏块的长度总结get_len_incl_bad函数的作⽤为:①如果偏移offset是从整块开始的,返回的结果是块的整数倍(不⽤看length,⽐如length是0x20001,则读两块)②如果偏移offset不是从整块开始的,返回的结果是块的整数倍+第⼀块要读的长度总之,返回的结果是按块补齐的再继续分析nand_read_skip_bad1if (len_incl_bad == *length) {2 rval = nand_read (nand, offset, length, buffer);3if (!rval || rval == -EUCLEAN)4return0;56return rval;7 }什么情况下,len_incl_bad == *length ?要读的内容⾥,没有坏块,且长度最后按块对齐(例如,从0x400开始读,读的长度为0x40000-0x400)如果不满⾜,则进⼊while循环读,在while⾥,按块读,先判断当前块是不是坏块,如果是则跳过,不是则读下⾯分析nand_read函数1/* 1.从哪读 2.读多少 3.读到哪去 */2static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,3 size_t *retlen, uint8_t *buf)4 {5struct nand_chip *chip = mtd->priv;6int ret;78/* Do not allow reads past end of device */9if ((from + len) > mtd->size)10return -EINVAL;11if (!len)12return0;13/* 选中芯⽚ */14 nand_get_device(chip, mtd, FL_READING);1516 chip->ops.len = len;17 chip->ops.datbuf = buf;18 chip->ops.oobbuf = NULL;1920 ret = nand_do_read_ops(mtd, from, &chip->ops);2122 *retlen = chip->ops.retlen;23/* 取消选中芯⽚ */24 nand_release_device(mtd);2526return ret;27 }其实真正的读函数是nand_do_read_ops,从哪去,读多少,读到哪去都被装载在chip->ops结构体中再看nand_do_read_ops函数1/**2 * nand_do_read_ops - [Internal] Read data with ECC3 *4 * @mtd: MTD device structure5 * @from: offset to read from6 * @ops: oob ops structure7 *8 * Internal function. Called with chip held.9*/10static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,11struct mtd_oob_ops *ops)12 {13int chipnr, page, realpage, col, bytes, aligned;14struct nand_chip *chip = mtd->priv;15struct mtd_ecc_stats stats;16int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;17int sndcmd = 1;18int ret = 0;19 uint32_t readlen = ops->len;//128K 0x20000 根据输⼊的长度决定20 uint32_t oobreadlen = ops->ooblen; // oobreadlen = 021 uint8_t *bufpoi, *oob, *buf;2223 stats = mtd->ecc_stats;2425 chipnr = (int)(from >> chip->chip_shift);26 chip->select_chip(mtd, chipnr);27/* realpage 总页地址(⾼17位) */28 realpage = (int)(from >> chip->page_shift);//pageshift is 1129 page = realpage & chip->pagemask;//pagemask = 1ffff30/* col 低11位 */31 col = (int)(from & (mtd->writesize - 1));//writesize = 2048, 2047 = 0x7ff32/* 得到页地址和列地址 */33 buf = ops->datbuf;34 oob = ops->oobbuf;3536while(1) {37 bytes = min(mtd->writesize - col, readlen); //128K 0x20000 根据输⼊的长度决定38 aligned = (bytes == mtd->writesize); //aligned = 1;3940/* Is the current page in the buffer ? */41if (realpage != chip->pagebuf || oob) {42 bufpoi = aligned ? buf : chip->buffers->databuf;43/* 对齐与不对齐读到的缓冲是不⼀样的 */44if (likely(sndcmd)) {45 chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);46 sndcmd = 0;47 }4849/* Now read the page into the buffer */50if (unlikely(ops->mode == MTD_OOB_RAW))51 ret = chip->ecc.read_page_raw(mtd, chip,//nand_read_page_raw52 bufpoi, page);//从哪⾥读,读到哪⾥去,读多少53else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)54 ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi);55/* nand_read_subpage */56else57 ret = chip->ecc.read_page(mtd, chip, bufpoi,//整页读 bufpoi = buf58 page); //nand_read_page_swecc59if (ret < 0)60break;6162/* Transfer not aligned data */63if (!aligned) {64if (!NAND_SUBPAGE_READ(chip) && !oob)65 chip->pagebuf = realpage;66 memcpy(buf, chip->buffers->databuf + col, bytes);67 }6869 buf += bytes;7071if (unlikely(oob)) {72/* Raw mode does data:oob:data:oob */73if (ops->mode != MTD_OOB_RAW) {74int toread = min(oobreadlen,75 chip->yout->oobavail);76if (toread) {77 oob = nand_transfer_oob(chip,78 oob, ops, toread);79 oobreadlen -= toread;80 }81 } else82 buf = nand_transfer_oob(chip,83 buf, ops, mtd->oobsize);84 }8586if (!(chip->options & NAND_NO_READRDY)) {87/*88 * Apply delay or wait for ready/busy pin. Do89 * this before the AUTOINCR check, so no90 * problems arise if a chip which does auto91 * increment is marked as NOAUTOINCR by the92 * board driver.93*/94if (!chip->dev_ready)95 udelay(chip->chip_delay);96else97 nand_wait_ready(mtd);98 }99 } else {100 memcpy(buf, chip->buffers->databuf + col, bytes);101 buf += bytes;102 }103104 readlen -= bytes;105106if (!readlen)107break;108109/* For subsequent reads align to page boundary. */110 col = 0;111/* Increment page address */112 realpage++;113114 page = realpage & chip->pagemask;115/* Check, if we cross a chip boundary */116if (!page) {117 chipnr++;118 chip->select_chip(mtd, -1);119 chip->select_chip(mtd, chipnr);120 }121122/* Check, if the chip supports auto page increment123 * or if we have hit a block boundary.124*/125if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))126 sndcmd = 1;127 }128129 ops->retlen = ops->len - (size_t) readlen;130if (oob)131 ops->oobretlen = ops->ooblen - oobreadlen;132133if (ret)134return ret;135136if (mtd->ecc_stats.failed - stats.failed)137return -EBADMSG;138139return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; 140 }。
Nandflash原理与启动详解
NandFlash原理与启动详解一、Nandflash内部是怎么工作的:1片Nandflash=1设备;1设备=4096块;1块=32页;1页=528字节=数据大小(512字节)+oob块大小(16字节)(oob用于Nandflash命令执行完成后设置状态)可以通过NAND Flash命令00h/01h/50h分别对前半部、后半部、OOB进行定位,通过NAND Flash内置的指针指向各自的首地址。
存储操作特点有:擦除操作的最小单位是块;NAND Flash芯片每一位只能从1变为0,而不能从0变为1,所以在对其进行写入操作之前一定要将相应块擦除(擦除即是将相应块的位全部变为1);OOB部分的第6字节(即517字节)标志是否是坏块,值为FF时不是坏块,否则为坏块。
除OOB第6字节外,通常至少把OOB的前3字节用来存放NAND Flash硬件ECC码。
(ECC:"Error Correcting Code" "错误检查纠正",带有奇偶校验的内存的主要功能。
)1.Nand flash以page为单位进行读写,以block为单位进行擦除,没页分为main区和spare区,main区用于存放正常的数据,spare区用于存放一些附加信息2.S3c2440 支持从Nand 启动是因为内部有一个叫做Steppingstone的SRAM buffer,当启动的时候,nand 的前4k的将会代码将被拷贝到steppingstone中执行,注意前4k代码是不会经过ECC校验的,所以必须确保这些代码的准确3.对nand的操作都是通过使用命令来实现,有的操作只要一个命令就可以完成,而有的需要两个命令才能完成,下面是K9F1G08U0B的命令表:4 Flash烧写程序原理及结构基本原理:将在SDRAM中的一段存储区域中的数据写到NAND Flash存储空间中。
烧写程序在纵向上分三层完成。
移植笔记-:从Nand_flash启动的uboot
到目前为此,这个版本的uboot已经加进了Nand驱动,可以使用Nand命令对nandflash进行读写。
但是还不能从Nand启动,只可以从Norflash启动或用tftp下载到sdram中然后go到该地址使用。
原因是目前移植的uboot还没有实现s3c2410支持的从Nand Flash启动机制需要的自拷贝功能。
下面介绍移植可以从Nand flash启动的uboot。
关于这里的Nand Boot概念,如果分析过vivi源码,明白vivi是如何从Nand跳转到sdram中继续执行以及copy_myself原理的话,再理解这里就比较简单了。
其实我们这里使uboot支持从nand启动的原理正是移植vivi里的copy_myself机制。
关于s3c2410从Nand Flash启动的原理可以查阅其他相关资料,不懂的话也没关系,不影响移植过程。
当然,我们的最终目的是编译一个可以直接烧录到nand中启动的uboot。
既然已经到这一步了,要实现nand启动已经很容易了。
只需要添加几行代码,大部分从vivi拷贝,只需修改少许地方。
过程如下:1.在board/smdk2410加入NAND Flash读函数,建立nand_read.c文件,加入如下内容(copy from vivi):(在vivi源码下的arch\s3c2410\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 1inline 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 */intnand_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;}2.修改cpu/arm920t/start.S文件(copy from vivi)(在vivi源码下的arch\s3c2410\Head.s。
Nand_flash工作原理
Nand flash芯片工作原理------------------------------------Nand flash芯片型号为Samsung K9F1208U0B,数据存储容量为64MB,采用块页式存储管理。
8个I/O引脚充当数据、地址、命令的复用端口。
芯片内部存储布局及存储操作特点:一片Nand flash为一个设备(device), 其数据存储分层为:1 (Device) = 4096 (Blocks)1 (Block) -= 32 (Pages/Rows) 页与行是相同的意思,叫法不一样1 (Page) = 528 (Bytes) = 数据块大小(512Bytes) + OOB 块大小(16Bytes)在每一页中,最后16个字节(又称OOB)用于Nand Flash命令执行完后设置状态用,剩余512个字节又分为前半部分和后半部分。
可以通过Nand Flash命令00h/01h/50h分别对前半部、后半部、OOB进行定位通过Nand Flash内置的指针指向各自的首地址。
存储操作特点:1. 擦除操作的最小单位是块。
2. Nand Flash芯片每一位(bit)只能从1变为0,而不能从0变为1,所以在对其进行写入操作之前要一定将相应块擦除(擦除即是将相应块得位全部变为1).3. OOB部分的第六字节(即517字节)标志是否是坏块,如果不是坏块该值为FF,否则为坏块。
(转载注:应该是每块的第一页的第六个字节。
)4. 除OOB第六字节外,通常至少把OOB的前3个字节存放Nand Flash硬件ECC码NAND FLASH的工作原理- to beginner2007-04-23 23:43NAND FLASH 是一种大容量、高速的存储技术。
其接口较为简单,如果没有专门的nand flash控制器,甚至可以用io口与之对接。
其编程也相对简单,只要了解如下关键概念就可以:1.nand flash内部有管理单元,管理单元负责对nand flash的实际单元的操作。
u_boot移植(五)之分析uboot源码中nand flash操作
u_boot移植(五)之分析uboot源码中nand flash操作一、OneNand 和Nand Flash我们已经能从Nand Flash启动了,启动之后,大家会看到如下效果:可以看出,我们的uboot默认使用的是OneNand。
需要注意的是我们的FSC100上面是没有OneNand的,有的是K9F2G08U0B型号的NAND FLASH。
前面我们了解过Nor Flash 和Nand Flash,那OneNand Flash又是什么呢?二、uboot 源码中Nand Flash部分代码分析我们从Nand Flash初始化看起,打开lib_arm/board.c文件,为了紧抓主线,以下代码只列举出了主线代码。
可以看出,我们可以通过CONFIG_CMD_NAND和CONFIG_CMD_ONENAND两个宏来选择NAND FLASH初始化还是 ONENAND FLASH初始化。
uboot 中默认定义了宏CONFIG_CMD_ONENAND,所以选择的是ONENAND FLASH初始化。
我们的FSC100上面使用的是NAND FLASH,所以我们要定义CONFIG_CMD_NAND宏,取消CONFIG_CMD_ONENAND宏的定义。
嗯!先做个记录:修改include/configs/fsc100.h,定义宏CONFIG_CMD_NAND,取消宏CONFIG_CMD_ONENAND。
好了,接下我们看看nand_init()函数时如何实现的。
看以看出,这段代码调用根据CONFIG_SYS_MAX_NAND_DEVICE宏[默认没有定义]的值来决定系统中Nand Flash设备的个数。
接着调用nand_init_chip()函数完成Nand Flash初始化,然后计算出每块Nand Flash的大小。
最终会输出Nand Flash总的容量。
嗯!做个记录:修改include/configs/fsc100.h,定义宏CONFIG_SYS_MAX_NAND_DEVICE,值为1没有看明白的地方是给nand_init_chip()函数传递的参数,接下来我们来看看他们是如何定义的。
Nand flash工作原理
Nand flash芯片工作原理------------------------------------Nand flash芯片型号为Samsung K9F1208U0B,数据存储容量为64MB,采用块页式存储管理。
8个I/O引脚充当数据、地址、命令的复用端口。
芯片内部存储布局及存储操作特点:一片Nand flash为一个设备(device), 其数据存储分层为:1 (Device) = 4096 (Blocks)1 (Block) -= 32 (Pages/Rows) 页与行是相同的意思,叫法不一样1 (Page) = 528 (Bytes) = 数据块大小(512Bytes) + OOB 块大小(16Bytes)在每一页中,最后16个字节(又称OOB)用于Nand Flash命令执行完后设置状态用,剩余512个字节又分为前半部分和后半部分。
可以通过Nand Flash命令00h/01h/50h分别对前半部、后半部、OOB进行定位通过Nand Flash内置的指针指向各自的首地址。
存储操作特点:1. 擦除操作的最小单位是块。
2. Nand Flash芯片每一位(bit)只能从1变为0,而不能从0变为1,所以在对其进行写入操作之前要一定将相应块擦除(擦除即是将相应块得位全部变为1).3. OOB部分的第六字节(即517字节)标志是否是坏块,如果不是坏块该值为FF,否则为坏块。
(转载注:应该是每块的第一页的第六个字节。
)4. 除OOB第六字节外,通常至少把OOB的前3个字节存放Nand Flash硬件ECC码NAND FLASH的工作原理- to beginner2007-04-23 23:43NAND FLASH 是一种大容量、高速的存储技术。
其接口较为简单,如果没有专门的nand flash控制器,甚至可以用io口与之对接。
其编程也相对简单,只要了解如下关键概念就可以:1.nand flash内部有管理单元,管理单元负责对nand flash的实际单元的操作。
UBOOT移植(NANDFLASH的支持)——初步移植(二)
UBOOT移植(NANDFLASH的支持)——初步移植(二)NAND FLASH初始化入口函数(arch/arm/lib/board.c)这里需要定义CONFIG_CMD_NAND这个宏才行,在配置头文件中已经包含进去了,没问题。
nand_init()函数(drivers/mtd/nand/nand.c)这个函数里面使用两个宏参数进行控制CONFIG_SYS_MAX_NAND_DEVICE和CONFIG_SYS_NAND_SELECT_DEVICE,前者已经在配置头文件中进行了描述,后者由于对应的是有多个NAND设备存在的情况,这里就忽略了。
nand_init_chip()函数(drivers/mtd/nand/nand.c)这个函数需要配置三个宏参数,除了CONFIG_RELOC_FIXUP_WORKS其余都定义了。
没有定义的那个宏给出的描述是“Relocation to SDRAM works on all XXX boards”意思是重置到SDRAM工作在所有XXX板子上。
UBOOT中大部分ARM板子上都没有定义这个宏,SMDK2410也没有定义这个宏,这里也就选择不定义。
这个函数函数还需要三个参数,这三个参数在开头进行了定义。
nand_info_t nand_info[CONFIG_SYS_MAX_NAND_DEVICE];static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE];static ulong base_address[CONFIG_SYS_MAX_NAND_DEVICE] =CONFIG_SYS_NAND_BASE_LIST;宏CONFIG_SYS_NAND_BASE_LIST采用默认的配置。
#ifndef CONFIG_SYS_NAND_BASE_LIST#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE }#endif上述所有参数在配置头文件中都有定义。
Nand Flash 工作原理
深入剖析NAND flash工作原理!NAND闪存阵列分为一系列128kB的区块(block),这些区块是NAND器件中最小的可擦除实体。
擦除一个区块就是把所有的位(bit)设置为"1"(而所有字节(byte)设置为FFh)。
有必要通过编程,将已擦除的位从"1"变为"0"。
最小的编程实体是字节(byte)。
一些NOR闪存能同时执行读写操作(见下图1)。
虽然NAND不能同时执行读写操作,它可以采用称为"映射(shadowing)"的方法,在系统级实现这一点。
这种方法在个人电脑上已经沿用多年,即将BIOS从速率较低的ROM加载到速率较高的RAM上。
NAND的效率较高,是因为NAND串中没有金属触点。
NAND闪存单元的大小比NOR要小(4F2:10F2)的原因,是NOR的每一个单元都需要独立的金属触点。
NAND与硬盘驱动器类似,基于扇区(页),适合于存储连续的数据,如图片、音频或个人电脑数据。
虽然通过把数据映射到RAM上,能在系统级实现随机存取,但是,这样做需要额外的RAM存储空间。
此外,跟硬盘一样,NAND器件存在坏的扇区,需要纠错码(ECC)来维持数据的完整性。
存储单元面积越小,裸片的面积也就越小。
在这种情况下,NAND就能够为当今的低成本消费市场提供存储容量更大的闪存产品。
NAND闪存用于几乎所有可擦除的存储卡。
NAND的复用接口为所有最新的器件和密度都提供了一种相似的引脚输出。
这种引脚输出使得设计工程师无须改变电路板的硬件设计,就能从更小的密度移植到更大密度的设计上。
NAND与NOR闪存比较NAND闪存的优点在于写(编程)和擦除操作的速率快,而NOR的优点是具有随机存取和对字节执行写(编程)操作的能力(见下图图2)。
NOR的随机存取能力支持直接代码执行(XiP),而这是嵌入式应用经常需要的一个功能。
NAND的缺点是随机存取的速率慢,NOR的缺点是受到读和擦除速度慢的性能制约。
NAND flash 工作原理
A0 ~ A7 : Selects the starting address of the 1st half of the register
A9 ~ A13 : Selects 1 page of 1 block (32 pages)
Chip Enable
/OE
Output Enable
/RESET
Hardware Reset Pin
RY/BY#
Ready/Busy Output
/WE
Write Enable
#WP/ACC Hardware Write Protection / Program Acceleration
PPrroodduuccttPPllaannnniinngg && AApppplliiccaattiioonn EEnngg.. TTeeaamm
FLASH : Flash Memory
Mask ROM : Mask Programmable Read Only Memory
OTP
: One Time Programmable ROM
PPrroodduuccttPPllaannnniinngg && AApppplliiccaattiioonn EEnngg.. TTeeaamm
EEPROM
• Single Power (3.3V or 5V)
• 2Tr 1Cell
Flash Memory
• Single or Dual Power (1.8V or 3.3V) • 1Tr 1Cell
UNIT CELL COMPARISON
EPROM
EEPROM
Flash Memory
Cell structure
UBOOT中关于NAND FLASH的支持
UBOOT中关于NAND FLASH的支持十分完善,从命令上可以看出来,关于NAND FLASH的操作专门有个子系统。
在驱动层面,UBOOT使用了MTD驱动规范,这个规范中对NAND FLASH的各种操作实现都很规范,一般来说只改写些少量的代码(相对于MTD 设备驱动来说)就可以支持起来。
大大减轻了移植难度。
一般来说,各个主控芯片的NAND FALSH驱动都存放于drivers/mtd/nand/目录之下,就S3C24X0系列芯片来说,UBOOT现在只提供了S3C2410的NAND FLASH驱动,而S3C2410和S3C2440在NAND FALSH模块方面是有一定的差别的,所以需要修改。
既然这里没有现成的驱动程序,就来实现一个吧,由于S3C2410和S3C2440同属于一个系列的芯片,所以就以S3C2410的驱动为模板写S3C2440的NAND FLASH驱动。
S3C2410的驱动(drivers/mtd/nand/s3c2410_nand.c)001#include <common.h>002003#include <nand.h>004#include <asm/arch/s3c24x0_cpu.h>005#include <asm/io.h>006007#define S3C2410_NFCONF_EN (1<<15)008#define S3C2410_NFCONF_512BYTE (1<<14)009#define S3C2410_NFCONF_4STEP (1<<13)010#define S3C2410_NFCONF_INITECC (1<<12)011#define S3C2410_NFCONF_nFCE (1<<11)012#define S3C2410_NFCONF_TACLS(x) ((x)<<8)013#define S3C2410_NFCONF_TWRPH0(x) ((x)<<4)014#define S3C2410_NFCONF_TWRPH1(x) ((x)<<0)015016#define S3C2410_ADDR_NALE 4017#define S3C2410_ADDR_NCLE 8018019#ifdef CONFIG_NAND_SPL020021/* in the early stage of NAND flash booting, printf() is not available */022#define printf(fmt, args...)023024static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)025{026int i;027struct nand_chip *this = mtd->priv;028029for (i = 0; i < len; i++)030buf[i] = readb(this->IO_ADDR_R);031}032#endif033034static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)035{036struct nand_chip *chip = mtd->priv;037struct s3c2410_nand *nand = s3c2410_get_base_nand();038039debugX(1, "hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);040041if (ctrl & NAND_CTRL_CHANGE)042{043ulong IO_ADDR_W = (ulong)nand;044045if (!(ctrl & NAND_CLE))046IO_ADDR_W |= S3C2410_ADDR_NCLE;047if (!(ctrl & NAND_ALE))048IO_ADDR_W |= S3C2410_ADDR_NALE;049050chip->IO_ADDR_W = (void *)IO_ADDR_W;051052if (ctrl & NAND_NCE)053writel(readl(&nand->NFCONF) & ~S3C2410_NFCONF_nFCE,054&nand->NFCONF);055else056writel(readl(&nand->NFCONF) | S3C2410_NFCONF_nFCE,057&nand->NFCONF);058}059060if (cmd != NAND_CMD_NONE)061writeb(cmd, chip->IO_ADDR_W);062}063064static int s3c2410_dev_ready(struct mtd_info *mtd)065{066struct s3c2410_nand *nand = s3c2410_get_base_nand();067debugX(1, "dev_ready\n");068return readl(&nand->NFSTAT) & 0x01;069}070071#ifdef CONFIG_S3C2410_NAND_HWECC072void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)073{074struct s3c2410_nand *nand = s3c2410_get_base_nand();075debugX(1, "s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode);076writel(readl(&nand->NFCONF) | S3C2410_NFCONF_INITECC, &nand->NFCONF);077}078079static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, 080u_char *ecc_code)081{082struct s3c2410_nand *nand = s3c2410_get_base_nand();083ecc_code[0] = readb(&nand->NFECC);084ecc_code[1] = readb(&nand->NFECC + 1);085ecc_code[2] = readb(&nand->NFECC + 2);086debugX(1, "s3c2410_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n", 087mtd , ecc_code[0], ecc_code[1], ecc_code[2]);088089return0;090}091092static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, 093u_char *read_ecc, u_char *calc_ecc)094{095if (read_ecc[0] == calc_ecc[0] &&096read_ecc[1] == calc_ecc[1] &&097read_ecc[2] == calc_ecc[2])098return0;099100printf("s3c2410_nand_correct_data: not implemented\n");101return -1;102}103#endif105int board_nand_init(struct nand_chip *nand)106{107u_int32_t cfg;108u_int8_t tacls, twrph0, twrph1;109struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();110struct s3c2410_nand *nand_reg = s3c2410_get_base_nand();111112debugX(1, "board_nand_init()\n");113114writel(readl(&clk_power->CLKCON) | (1 << 4), &clk_power->CLKCON);115116/* initialize hardware */117twrph0 = 3;118twrph1 = 0;119tacls = 0;120121cfg = S3C2410_NFCONF_EN;122cfg |= S3C2410_NFCONF_TACLS(tacls - 1);123cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);124cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);125writel(cfg, &nand_reg->NFCONF);126127/* initialize nand_chip data structure */128nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)&nand_reg->NFDATA;129130nand->select_chip = NULL;131132/* read_buf and write_buf are default */133/* read_byte and write_byte are default */134#ifdef CONFIG_NAND_SPL135nand->read_buf = nand_read_buf;136#endif137138/* hwcontrol always must be implemented */139nand->cmd_ctrl = s3c2410_hwcontrol;140141nand->dev_ready = s3c2410_dev_ready;142143#ifdef CONFIG_S3C2410_NAND_HWECC144nand->ecc.hwctl = s3c2410_nand_enable_hwecc;145nand->ecc.calculate = s3c2410_nand_calculate_ecc;146nand->ecc.correct = s3c2410_nand_correct_data;147nand->ecc.mode = NAND_ECC_HW;148nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;149nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;150#else151nand->ecc.mode = NAND_ECC_SOFT;152#endif153154#ifdef CONFIG_S3C2410_NAND_BBT155nand->options = NAND_USE_FLASH_BBT;156#else157nand->options = 0;158#endif159160debugX(1, "end of nand_init\n");162return0;163 }可以看到在这个驱动中,为了让UBOOT在S3C2410下支持NAND FLASH实现了如下函数。
6.Uboot启动分析
6.Uboot启动分析展开全文6. Uboot启动分析6.1. 硬件引导通常在上电后,一旦CPU电源稳定期结束,它则开始从某个固定的地址(通常为FLASH的0地址)开始执行程序,为何方便代码的引导,一些CPU支持多种方式/地址处的引导,比如设置跳线,可以选择NOR Flash,或者NAND FLASH等。
S3C6410就是这样一款功能强大的CPU。
S3C6410 具备了一个内部SRAM缓冲器,大小为8K,叫做“STEPPINGSTONE”,如果跳线设置为从Nand Flash启动,则当系统启动时,Nand Flash存储器的前4KB 将被自动载入到STEPPINGSTONE中,然后系统自动执行这些载入的引导代码。
而Nand Flash的开始就是放置的U-boot的镜像。
对于S3C6410来说,起始代码位于:cpu/s3c64xx/start.S.globl _start /* uboot启动入口*/_start: b reset /* 复位向量*/ldr pc, _undefined_instructionldr pc, _software_interruptldr pc, _prefetch_abortldr pc, _data_abortldr pc, _not_usedldr pc, _irq /* 中断向量 */ldr pc, _fiq /* 中断向量 */......第一跳指令就是复位:/* 系统上电或reset后,cpu的PC一般都指向0x0地址,在0x0地址上的指令是 */reset:/** set the cpu to SVC32 mode*/mrs r0, cpsr/**把CPSR内容存入r0.使用了mrs指令:专用寄存器到通过寄存器的存取.*CPSR当前程序状态寄存器格式如下:** 31 30 29 28 27 26 25 24 ~ ~ ~ 8 7 6 5 4 3 2 1 0*___ ___ ___ ___ ___ ___ ___ ___ _ _ _ _ ___ ___ ___ ____ ____ ____ ____ ____*| N | Z | C | V | * | * | * | * | * * * | I | F | T | M4 | M3 | M2 | M1 | M0 |**//* bic指令(bit clear): r0:= r0 and (not op2).上边的指令目的是把bit0~bit4清零.*/bic r0,r0,#0x1forr r0,r0,#0xd3/* r0:= r0 or 0xd3 . 以上三条指令执行后r0值为:**** **** **** **** **** ***** 11*1 0011 */msr cpsr,r0msr把r0存于cpsr.注意:msr指令是专用的通用寄存器到特殊功能寄存器的指令与mrs对应说明:通过上边的指令可以看到,实现了两个功能.1,disable 外部中断(IRQ)与快速中断(FIR).2,把系统设为SVC32状态(超级保护)即M4~M1=10011。
u_boot移植(四)之nand flash启动
u_boot移植(四)之nand flash启动通过前面几节的介绍,我们了解了DDR2内存的初始化,K9F2G08U0B Nand Flash的操作。
接下来,是时候将我们的uboot烧写到Nand Flash,然后从Nand Flash启动了。
在正式干活之前,我们先来了解一下S5PC100的启动流程。
一、S5PC100 启动流程通过上面英文的解释,我们可以知道:(1)S5PC100在上电的时候,可以选择从Nand Flash 、OneNand 、SD 卡、USB进行启动。
我们这里选择从Nand Flash进行启动。
(2)S5PC100上电的时候,首先运行的是它内部ROM中程序。
这段程序会自动拷贝Nand Flash前16KB数据到S5PC100内部的SRAM中,接下来跳到内部的SRAM中执行。
我们如果想将我们的uboot能从NAND FLASH启动,就必须把它烧写到Nand Flash的启始位置。
这样以一上电的时候,S5PC100内部ROM中的程序会将我们uboot的前16KB代码拷贝到其内部的SRAM中执行。
嗯,这样我们就可以执行uboot的第一阶段代码了。
通过前面我们对NAND FLASH启动流程的分析,我想大家应该知道,uboot第一阶段干的主要事情有三个:(1)初始化DRAM(2)拷贝Nand Flash上的uboot到DRAM中(3)跳到DRAM中执行Nand Flash的第二阶段下面,我画了一幅图,对上面的过程做了简单的一个总结,帮助大家更好的理解,uboot是如何从Nand Flash启动的?好了,接下来我们就来修改uboot源码,让它支持从nand flash启动。
二、修改uboot源码,支持从nand flash启动这里主要修改的是u_boot的第一阶段,我想通过前面对uboot的启动流程分析,大家应该很熟悉uboot的第一阶段了。
好,开始干活吧!我们回顾一下,第一阶段设计到主要文件:需要关心的文件我已经在上图中标识出来了,接下来我们来看一下每个文件,确认需要修改的地方。
uboot从nand启动介绍
基于smdk2410 开发板u-boot-1.2.0 对nand flash的支持1.预备知识1.1. Nand Flash VS Nor Flash性能比较:flash闪存是非易失存储器,可以对称为块的存储器单元块进行擦写和再编程。
任何flash器件的写入操作只能在空或已擦除的单元内进行,所以大多数情况下,在进行写入操作之前必须先执行擦除。
NAND器件执行擦除操作是十分简单的,而NOR则要求在进行擦除前先要将目标块内所有的位都写为0。
由于擦除NOR器件时是以64~128KB的块进行的,执行一个写入/擦除操作的时间为5s,与此相反,擦除NAND器件是以8~32KB的块进行的,执行相同的操作最多只需要4ms。
执行擦除时块尺寸的不同进一步拉大了NOR和NADN之间的性能差距,统计表明,对于给定的一套写入操作(尤其是更新小文件时更多的擦除操作必须在基于NOR的单元中进行。
这样,当选择存储解决方案时,必须权衡以下的各项因素。
● NOR的读速度比NAND稍快一些。
● NAND的写入速度比NOR快很多。
● NAND的4ms擦除速度远比NOR的5s快。
●大多数写入操作需要先进行擦除操作。
● NAND的擦除单元更小,相应的擦除电路更少。
接口差别:NOR flash带有SRAM接口,有足够的地址引脚来寻址,可以很容易地存取其内部的每一个字节。
NAND器件使用复杂的I/O口来串行地存取数据,各个产品或厂商的方法可能各不相同。
8个引脚用来传送控制、地址和数据信息。
NAND读和写操作采用512字节的块,这一点有点像硬盘管理此类操作,很自然地,基于NAND的存储器就可以取代硬盘或其他块设备。
容量和成本NAND flash的单元尺寸几乎是NOR器件的一半,由于生产过程更为简单,NAND结构可以在给定的模具尺寸内提供更高的容量,也就相应地降低了价格。
NOR flash占据了容量为1~16MB闪存市场的大部分,而NAND flash只是用在8~128MB的产品当中,这也说明NOR主要应用在代码存储介质中,NAND适合于数据存储,NAND在CompactFlash、Secure Digital、PC Cards和MMC存储卡市场上所占份额最大。
从NandFlash启动U-BOOT的基本原理
从Nand Flash启动U-BOOT的基本原理前4K的问题如果S3C2410被配置成从Nand Flash启动(配置由硬件工程师在电路板设置), S3C2410的Nand Flash控制器有一个特殊的功能,在S3C2410上电后,Nand Flash控制器会自动的把Nand Flash上的前4K数据搬移到4K内部RAM中,并把0x00000000设置内部RAM的起始地址,CPU从内部RAM的0x00000000位置开始启动。
这个过程不需要程序干涉。
程序员需要完成的工作,是把最核心的启动程序放在Nand Flash的前4K中。
启动程序的安排由于Nand Flash控制器从Nand Flash中搬移到内部RAM的代码是有限的,所以在启动代码的前4K里,我们必须完成S3C2410的核心配置以及把启动代码(U-BOOT)剩余部分搬到RAM 中运行。
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.Nand Flash的命令、地址、数据都通过I/O口发送,管脚复用,这样做做的好处是,可以明显减少NAND FLASH的管脚数目,将来如果设计者想将NAND FLASH更换为更高密度、更大容量的,也不必改动电路板。
NAND FLASH不能够执行程序,本人总结其原因如下:1. NAND FLASH本身是连接到了控制器上而不是系统总线上。
CPU启动后是要取指令执行的,如果是SROM、NOR FLASH 等之类的,CPU 发个地址就可以取得指令并执行,NAND FLASH不行,因为NAND FLASH 是管脚复用,它有自己的一套时序,这样CPU无法取得可以执行的代码,也就不能初始化系统了。
UBOOT从NAND FLASH启动分析
UBOOT从NAND FLASH启动分析UBOOT从NAND FLASH启动分析在分析启动代码之前先看一下S3C2440的NAND启动:在配置NAND启动模式之后,S3C2440上电会先将NAND中的0x0 - 0x1000共4096字节的数据拷贝到位于Bank0中的Boot Internal SRAM上Bank0如下图:可以看出Boot Internal SRAM为4KB大小,也正是因为Boot Internal SRAM只有4KB 大小,所以只能从NAND中拷贝4K的内容= 3= 这个Boot Internal SRAM是配置为NAND FLASH启动模式才有的这4K内容是什么呢?~ 这就要看Uboot的镜像文件中是如何进行连接的了~连接脚本在board/smdk2440/u-boot.lds中,如下SECTIONS{. = 0x00000000;. = ALIGN(4);.text :{cpu/arm920t/start.o (.text)cpu/arm920t/s3c24x0/nand_read.o (.text)*(.text)}. = ALIGN(4);.rodata : { *(.rodata) }. = ALIGN(4);.data : { *(.data) }. = ALIGN(4);.got : { *(.got) }. = .;__u_boot_cmd_start = .;.u_boot_cmd : { *(.u_boot_cmd) }__u_boot_cmd_end = .;. = ALIGN(4);__bss_start = .;.bss : { *(.bss) }_end = .;}.text为代码段,可以看出cpu/arm920t/start.o在代码段的最前面,所以会先执行start.o 中的代码连接完成后的镜像文件的前4K如下cpu/arm920t/start.o(.text).text 0x33f80000 0x4e0 cpu/arm920t/start.o0x33f80050 IRQ_STACK_START0x33f80048 _bss_start0x33f8004c _bss_end0x33f80044 _armboot_start0x33f80000 _start0x33f80054 FIQ_STACK_STARTcpu/arm920t/s3c24x0/nand_read.o(.text).text 0x33f804e0 0x1b8 cpu/arm920t/s3c24x0/nand_read.o0x33f804e0 nand_read_ll*(.text).text 0x33f80698 0x64 board/smdk2440/libsmdk2440.a(lowlevel_init.o)0x33f8069c lowlevel_init.text 0x33f806fc 0x280 cpu/arm920t/libarm920t.a(interrupts.o)0x33f80934 do_fiq0x33f80880 do_undefined_instruction0x33f80744 show_regs0x33f80958 do_irq0x33f80728 bad_mode0x33f808c8 do_prefetch_abort0x33f8070c disable_interrupts0x33f80910 do_not_used0x33f808ec do_data_abort0x33f808a4 do_software_interrupt0x33f806fc enable_interrupts.text 0x33f8097c 0x250 cpu/arm920t/s3c24x0/libs3c24x0.a(interrupts.o)0x33f80aa4 set_timer0x33f80a20 reset_timer0x33f8097c interrupt_init0x33f80ba0 get_tbclk0x33f80a90 get_timer0x33f809f0 reset_timer_masked0x33f80a24 get_timer_masked0x33f80ab4 udelay0x33f80b10 udelay_masked0x33f80bac reset_cpu0x33f80b8c get_ticks.text 0x33f80bcc 0x150 cpu/arm920t/s3c24x0/libs3c24x0.a(speed.o)0x33f80c4c get_HCLK0x33f80cec get_PCLK0x33f80c44 get_FCLK0x33f80d14 get_UCLK.text 0x33f80d1c 0x1e8 cpu/arm920t/s3c24x0/libs3c24x0.a(cmd_s3c24xx.o) 0x33f80d8c do_s3c24xx.text 0x33f80f04 0xdc cpu/arm920t/s3c24x0/libs3c24x0.a(serial.o)0x33f80f04 serial_setbrg0x33f80fa8 serial_tstc0x33f80f80 serial_putc0x33f80f58 serial_init0x33f80fb8 serial_puts0x33f80f68 serial_getc.text 0x33f80fe0 0x140 lib_arm/libarm.a(_divsi3.o)0x33f80fe0 __divsi3如何设置从0x33f80000开始呢?~这是链接的时候指定的在根目录下面的config.mk中有下面一句LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)关键就是其中的-Ttext $(TEXT_BASE),这句指明了代码段的起始地址而TEXT_BASE在board/smdk2440/config.mk中定义TEXT_BASE = 0x33F8 0000 为什么是0x33F8 0000呢?~这是将NAND中Uboot拷贝到RAM中的起始地址,所以在代码拷贝到RAM之前不能使用绝对地址来寻址数据,只能用相对地址在以下将用虚拟地址来指Uboot在RAM中的地址,也就是0x33F8 0000现在来看代码cpu/arm920t/start.S_start: ;异常处理向量表b start_codeldr pc, _undefined_instruction ;未定义指令异常:0x00000004ldr pc, _software_interrupt ;软中断异常:0x00000008ldr pc, _prefetch_abort ;预取异常:0x0000000Cldr pc, _data_abort ;数据异常:0x00000010ldr pc, _not_used ;未使用:0x00000014ldr pc, _irq ;外部中断请求IRQ:0x00000018ldr pc, _fiq ;快束中断请求FIQ:0x0000001Cb start_code在虚拟地址0x33F8 0000处, 拷贝到Boot Internal SRAM后则位于0x0处,所以b start_code是第一条执行的指令,start_code在cpu/arm920t/start.S中代码如下://读取CPSR寄存器的内容到R0mrs r0,cpsr//清除R0中的0 - 4 这5个位后保存到R0中//也就是清除用户模式位bic r0,r0,#0x1f//置R0的0 1 4 6 7 位为真//也就是选择SVC模式,同时IRQ和FIQ被禁止,处理器处于ARM状态//关闭中断和快速中断orr r0,r0,#0xd3//将R0中的值保存到CPSR上msr cpsr,r0# define pWTCON 0x53000000 ;看门狗控制寄存器WTCON# define INTMSK 0x4A000008 ;中断屏蔽寄存器INTMSK# define INTSUBMSK 0x4A00001C ;辅助中断屏蔽寄存器,由于外设中断源太多,要用此寄存器屏蔽剩余的中断源# define LOCKTIME 0x4c000000 ;PLL锁定时间计数寄存器# define MPLLCON 0x4c000004 ;主时钟锁相环控制寄存器# define UPLLCON 0x4c000008# define CLKDIVN 0x4C000014 ;时钟分频寄存器/* clock divisor register */# define INTSUBMSK_val 0xffff# define MPLLCON_val ((184 12) + (2 4) + 2) /*406M*/# define UPLLCON_val ((60 12) + (4 4) + 2) /* 47M */# define CLKDIVN_val 7 /* FCLK:HCLK:PCLK = 1:3:6 */# define CAMDIVN 0x4C000018//取得看门狗寄存器的地址ldr r0, =pWTCON//将R1寄存器清0mov r1, #0x0//将看门狗寄存器清0,即将看门狗禁止,包括定时器定时,溢出中断及溢出复位等str r1, [r0]/** mask all IRQs by setting all bits in the INTMR - default*///设R1寄存器为0xFFFF FFFFmov r1, #0xffffffff//读取中断屏蔽寄存器的地址ldr r0, =INTMSK//将中断屏蔽寄存器中的位全设1,屏蔽所有中断str r1, [r0]//# define INTSUBMSK_val 0xffff//设R1寄存器为0xFFFFldr r1, =INTSUBMSK_val//读取辅助中断屏蔽寄存器的地址ldr r0, =INTSUBMSK//将辅助中断屏蔽寄中的11个中断信号屏蔽掉,本人觉得INTSUBMS_val应设成7ff str r1, [r0]//# define LOCKTIME 0x4c000000//读取PLL锁频计数器寄存器地址到R0中ldr r0,=LOCKTIME//将R1设为0x00FF FFFFldr r1,=0xffffff//M_LTIME为最大的0xFFF//U_LTIME为最大的0xFFFstr r1,[r0] ;0xfff=4096>1800,远远满足锁定要求/* FCLK:HCLK:PCLK = 1:2:4 *//* default FCLK is 120 MHz ! *///# define CLKDIVN 0x4C000014 /* clock divisor register *///读取时钟分频寄存器的地址ldr r0, =CLKDIVN//# define CLKDIVN_val 7 /* FCLK:HCLK:PCLK = 1:3:6 *///将R1设为0x7mov r1, #CLKDIVN_va//PDIVN - 1: PCLK has the clock same as the HCLK/2.//HDIVN - 11 : HCLK = FCLK/3 when CAMDIVN[8] = 0.// HCLK = FCLK/6 when CAMDIVN[8] = 1.str r1, [r0]/* Make sure we get FCLK:HCLK:PCLK = 1:3:6 *///# define CAMDIVN 0x4C000018//读取摄像头时钟分频寄存器的地址ldr r0, =CAMDIVN//将R1设为0mov r1, #0//将摄像头时钟分频寄存器清0str r1, [r0]/* Clock asynchronous mode *///MRC p15, 0, Rd, c1, c0, 0 ; read control register//读取控制寄存器中的值到R1中mrc p15, 0, r1, c1, c0, 0 ;将协处理器p15的寄存器c1和c0的值传到arm处理器的R1寄存器中//31 iA bit Asynchronous clock select//30 nF bit notFastBus selectorr r1, r1, #0xc0000000 ;将最高两位置1//MCR p15, 0, Rd, c1, c0, 0 ; write control register//将R1中的值写到控制寄存器中mcr p15, 0, r1, c1, c0, 0 将arm的寄存器R1的32位数据传到协处理器p15的两个16位寄存器c1和c0//# define UPLLCON 0x4c000008//读取UPLL设置寄存器的地址到R0中ldr r0,=UPLLCON//# define UPLLCON_val ((60ldr r1,=UPLLCON_val//将R1中的值写入UPLL设置寄存器中str r1,[r0]//ARM920T为5级流水线,需要至少5个周期来让指令生效nopnopnopnopnopnopnopnop//读取MPLL设置寄存器的地址到R0中ldr r0,=MPLLCON//# define MPLLCON_val ((184ldr r1,=MPLLCON_val//将R1中的值写入MPLL设置寄存器中str r1,[r0]#define GPJCON 0x560000D0#define GPJDAT 0x560000D4#define GPJUP 0x560000D8//跳转到cpu_init_crit处执行//并将下一条指令的地址写入LR寄存器中bl cpu_init_critcpu_init_crit在cpu/arm920t/start.S中代码如下:cpu_init_crit:/** flush v4 I/D caches*///将R0寄存器置0mov r0, #0//Invalidate ICache and DCache SBZ MCR p15,0,Rd,c7,c7,0 //禁止指令和数据cachemcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache *///Invalidate TLB(s) SBZ MCR p15,0,Rd,c8,c7,0mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB *//** disable MMU stuff and caches*///MRC p15, 0, Rd, c1, c0, 0 ; read control registermrc p15, 0, r0, c1, c0, 0//清除[8] [9] [13] 这3个位//8 - System protection//9 - ROM protection//13 - Base location of exception registers - 0 = Low addresses = 0x00000000. bic r0, r0, #0x00002300 // clear bits 13, 9:8 (--V- --RS)//清除[0] [1] [2] [7] 这4个位// 0 - MMU enable - 0 = MMU disabled.// 1 - Alignment fault enable - 0 = Fault checking disabled.// 2 - DCache enable - 0 = DCache disabled.// 7 - Endianness - 0 = Little-endian operation.bic r0, r0, #0x00000087 // clear bits 7, 2:0 (B--- -CAM)//设置位[1]为真// 1 - Alignment fault enable - 1 = Fault checking enabled.orr r0, r0, #0x00000002 // set bit 2 (A) Align//设置位[12]为真//12 - ICache enable - 1 = ICache enabled.orr r0, r0, #0x00001000 // set bit 12 (I) I-Cache//MCR p15, 0, Rd, c1, c0, 0 ; write control registermcr p15, 0, r0, c1, c0, 0//将返回地址保存到IP中mov ip, lr//跳转到lowlevel_init中执行bl lowlevel_initcpu_init_crit在cpu/arm920t/start.S中代码如下:.globl lowlevel_init//读取下面标号为SMRDATA处的地址到R0中ldr r0, =SMRDATA//读取上面标号为_TEXT_BASE处的地址内容到R1中//也就是取得TEXT_BASE的值到R1中ldr r1, _TEXT_BASE//计算SMRDATA的相对地址保存到R0中//SMRDATA为虚拟地址,而TEXT_BASE为虚拟地址的起始地址//而现在Uboot的起始地址并不为虚拟地址//TEXT_BASE为0x33F8 0000,SMRDATA为0x33F8 06C8//而现在程序运行在起始地址为0x0000 0000的地方//所以需要计算以0x0000 0000为标准的相对地址sub r0, r0, r1//取得带宽与等待状态控制寄存器地址到R1中ldr r1, =BWSCON /* Bus Width Status Controller *///一共需要设置13个寄存器,每个寄存器4字节add r2, r0, #13*40://读取R0所指的项的值到R3中后R0自加4字节ldr r3, [r0], #4//将R3中的值保存到R1所指的地址中后R1自加4字节str r3, [r1], #4//比较R0和R2是否相等,相等则说明13个寄存器全部设置完毕cmp r2, r0//不等则跳转到上面标号为0处的地址继续执行bne 0b//跳回到返回地址中继续执行mov pc, lr.ltorg/* the literal pools origin */SMRDATA:.word(0+(B1_BWSCON4)+(B2_BWSCON8)+(B3_BWSCON12)+(B4_BWSCON16)+( B5_BWSCON20)+(B6_BWSCON24)+(B7_BWSCON28)).word((B0_Tacs13)+(B0_Tcos11)+(B0_T acc8)+(B0_Tcoh6)+(B0_T ah4)+(B0_T acp2) +(B0_PMC)).word((B1_Tacs13)+(B1_Tcos11)+(B1_T acc8)+(B1_Tcoh6)+(B1_T ah4)+(B1_T acp2) +(B1_PMC)).word((B2_Tacs13)+(B2_Tcos11)+(B2_T acc8)+(B2_Tcoh6)+(B2_T ah4)+(B2_T acp2) +(B2_PMC)).word((B3_Tacs13)+(B3_Tcos11)+(B3_T acc8)+(B3_Tcoh6)+(B3_T ah4)+(B3_T acp2) +(B3_PMC)).word((B4_Tacs13)+(B4_Tcos11)+(B4_T acc8)+(B4_Tcoh6)+(B4_T ah4)+(B4_T acp2) +(B4_PMC)).word((B5_Tacs13)+(B5_Tcos11)+(B5_T acc8)+(B5_Tcoh6)+(B5_T ah4)+(B5_T acp2) +(B5_PMC)).word ((B6_MT15)+(B6_Trcd2)+(B6_SCAN)).word ((B7_MT15)+(B7_Trcd2)+(B7_SCAN)).word ((REFEN23)+(TREFMD22)+(Trp20)+(Trc18)+(Tchr16)+REFCNT).word 0x32.word 0x30.word 0x30执行mov pc, lr后将返回到cpu_init_crit中剩下来还有2条指令//恢复返回地址到LRmov lr, ip//跳转到返回地址mov pc, lr执行完毕之后将返回到start_code中执行接下来的代码代码如下://#define GPJCON 0x560000D0//取得J端口控制寄存器的地址到R0中LDR R0, = GPJCON//将R1设置为0x1 5555LDR R1, = 0x15555//将R1中的值保存到J端口控制寄存器//GPJ0 - 01 - Output//GPJ1 - 01 - Output//GPJ2 - 01 - Output//GPJ3 - 01 - Output//GPJ4 - 01 - OutputSTR R1, [R0]//#define GPJUP 0x560000D8//取得J端口上拉功能寄存器的地址到R0中LDR R0, = GPJUP//将R1设置为0x1FLDR R1, = 0x1f//将R1中的值保存到J端口上拉功能寄存器//禁止GPJ0 - GPJ4的上拉功能STR R1, [R0]//#define GPJDAT 0x560000D4//取得J端口数据寄存器的地址到R0中LDR R0, = GPJDAT//将R1设为0x0LDR R1, = 0x00//将R1中的值保存到J端口数据寄存器//将J端口数据寄存器清0STR R1, [R0]//下面是NAND数据拷贝过程//relocate:copy_myself://#define S3C2440_NAND_BASE 0x4E000000//取得Nand Flash设置寄存器的地址mov r1, #S3C2440_NAND_BASE//将R2设为0xFFF0ldr r2, =0xfff0 // initial value tacls=3,rph0=7,rph1=7 //#define oNFCONF 0x00//读取Nand Flash设置寄存器中的值到R3中ldr r3, [r1, #oNFCONF]//将R3或上R2后保存到R3中orr r3, r3, r2//将R3中的值保存到Nand Flash设置寄存器中//TWRPH0 - 111 - Duration = HCLK * (TWRPH0 + 1) //TACLS - 11 - Duration = HCLK * TACLSstr r3, [r1, #oNFCONF]//#define oNFCONT 0x04//读取Nand Flash控制寄存器中的值到R3中ldr r3, [r1, #oNFCONT]//将R3的[0]位置1orr r3, r3, #1 // enable nand controller//将R3中的值保存到Nand Flash控制寄存器中//Mode - 1:Nand Flash Controller Enablestr r3, [r1, #oNFCONT]//读取虚拟起始地址到R0中ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot *///预留malloc所需要的空间sub r0, r0, #CFG_MALLOC_LEN /* malloc area *///预留bdinfo所需要的空间sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo *///预留中断和快速中断向量表空间sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)//预留12字节给中断栈sub sp, r0, #12 /* leave 3 words for abort-stack */// copy u-boot to RAM//读取虚拟起始地址到R0中,作为目标地址ldr r0, _TEXT_BASE//将R1设为0,作为源地址mov r1, #0x0//将UBOOT大小的值保存在R2中,作为数据大小mov r2, #CFG_UBOOT_SIZE//跳转到nand_read_ll处执行//并将下一条指令的地址保存在LR中bl nand_read_llnand_read_ll的原型为int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size) 之前设置的R0 R1 R2为它的3个参数R0 - bufR1 - start_addrR2 - sizenand_read_ll的代码在cpu/arm920t/s3c24x0/nand_read.c中int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size) {int i, j;//检测源地址和大小是否在NandFlash的边界上if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) //不在边界上则返回-1表示出错return -1; /* invalid alignment *//* chip Enable */// #define nand_select() (NFCONT &= ~(1//置NAND Flash控制寄存器中除Reg_nCE外所有的位为1//Reg_nCE - NAND FLASH Memory nFCE signal control//0 - Force nFCE to low (Enable chip select)nand_select();// #define nand_clear_RnB() (NFSTAT |= (1//置NAND Flash操作状态寄存器中的RnB_TransDetect位为1//When RnB low to high transition is occurred, this value set and issue interrupt if enabled.//To clear this value write '1'//1: RnB transition is detectednand_clear_RnB();for (i=0; i10; i++);//从源地址的首地址开始历便所要拷贝的数据大小for (i=start_addr; i (start_addr + size);){//检测地址是否在NAND Flash的边界上if (start_addr % NAND_BLOCK_SIZE == 0){//检测是否为坏块if (is_bad_block(i)){/* Bad block *///向后延伸一个存储块i += NAND_BLOCK_SIZE;size += NAND_BLOCK_SIZE;//跳到下一块continue;}}j = nand_read_page_ll(buf, i);//指向下一块i += j;buf += j;// LED_FLASH();}/* chip Disable */// #define nand_deselect() (NFCONT |= (1//置Reg_nCE位为1//NAND Flash Memory nFCE signal control//1: Force nFCE to High(Disable chip select)nand_deselect();return 0;}nand_read_ll将Uboot从NAND中拷贝到RAM中拷贝完成后将返回到start_code接下来的代码如下://检测R0是否为0,R0为nand_read_ll的返回值tst r0, #0x0//为0则说明无错,跳转到ok_nand_read处执行beq ok_nand_readok_nand_read://将R0设为0mov r0, #0//ldr r1, =0x33f00000//将R1设为虚拟地址起始处ldr r1, _TEXT_BASE//检测0x400个字节mov r2, #0x400 // 4 bytes * 1024 = 4K-bytesgo_next://读取R0处地址的数据到R3中//然后R0自加4字节ldr r3, [r0], #4//读取R1处地址的数据到R4中//然后R1自加4字节ldr r4, [r1], #4//比较R3和R4的数据是否相等//也就是检测Boot Internal SRAM和RAM中的数据是否相等//以保证数据无错teq r3, r4//不等则跳转到notmatchbne notmatch//相等则R2自减4subs r2, r2, #4//当R2为0则跳转到done_nand_readbeq done_nand_read//R2不为0则跳转回go_next继续检测bne go_nextdone_nand_read:LDR R0, = GPJDATLDR R1, = 0x2STR R1, [R0]stack_setup://读取虚拟起始地址到R0中ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */ //预留malloc所需要的空间sub r0, r0, #CFG_MALLOC_LEN /* malloc area *///预留bdinfo所需要的空间sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo *///预留中断和快速中断向量表空间sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)//预留12字节给中断栈sub sp, r0, #12 /* leave 3 words for abort-stack */clear_bss://读取BSS段的起始地址ldr r0, _bss_start /* find start of bss segment *///读取BSS段的结束地址ldr r1, _bss_end /* stop here *///将R2设为0x0mov r2, #0x00000000 /* clear */clbss_l://将R2中的值保存在R0所指的地址str r2, [r0] /* clear loop... *///R0自加4字节add r0, r0, #4//比较R0和R1是否相等cmp r0, r1//不等则说明清0还没结束ble clbss_lLDR R0, = GPJDATLDR R1, = 0x1STR R1, [R0]//跳转到start_armboot处执行ldr pc, _start_armboot_start_armboot: .word start_armboot这里start_armboot是一个绝对地址,在朗成所修改的这个Uboot中为0x33F8 13F4 执行ldr pc, _start_armboot之后将会跳到RAM中的绝对地址继续执行整理了一个流程图,分为3个存储器:1 Boot Internal SRAM , 接在BANK0,起始地址为0x02 RAM , 接在BANK6,起始地址为0x3000 00003 NAND FLASH,为单独寻址流程如下图:红字为流程序号:1. 首先将NAND FLASH中的前0x1000字节内容拷贝到Boot Internal SRAM中2. 从Boot Internal SRAM的0x0地址处开始执行指令3. 将Uboot从Flash拷贝到RAM中4. 执行ldr pc, _start_armboot从Boot Internal SRAM中跳转到RAM中的绝对地址0x33F8 13F4处继续执行。
u-boot移植5--从nandflash启动甄选
u-boot移植5--从nandflash启动(优选.)Uboot移植5--支持从nand flash启动同时支持norflash和nandflash启动方法一:在时钟修改方法一条件下:修改cpu/arm920t/start.S/*#ifndef CONFIG_SKIP_RELOCATE_UBOOTrelocate: */ /* 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 /* don't reloc during debug */beq stack_setup/*check code position*/ldr r0,=48000000ldr r0,[r0]ands r0,r0,#6bne nor_boot#define LENGTH_UBOOT 0x60000#define NAND_CTL_BASE 0x4e000000/*offset*/#define oNFCONF 0x00#define oNFCONT 0x04#define oNFCMD 0x08#define oNFSTAT 0x20nand_boot:mov r1,#NAND_CTL_BASEldr r2,=((7<<12)|(7<<8)|(7<<4))str r2,[r1,#oNFCONF]ldr r2,[r1,#oNFCONF]ldr r2,=((1<<4)|(0<<1)|(1<<0))str r2,[r1,#oNFCONT]ldr r2,[r1,#oNFCONT]ldr r2,=(0x6)str r2,[r1,#oNFSTAT]ldr r2,[r1,#oNFSTAT]mov r2,#0xffstr r2,[r1,#oNFCMD]mov r3,#0nand1:add r3,r3,#0x1cmp r3,#0xablt nand1nand2:ldr r2,[r1,#oNFSTAT]tst r2,#0x4beq nand2ldr r2,[r1,#oNFCONT]orr r2,r2,#0x2str r2,[r1,#oNFCONT]ldr sp, DW_STACK_STARTmov fp, #0ldr r0, =TEXT_BASEmov r1, #0x0mov r2, #LENGTH_UBOOTbl nand_read_lltst r0, #0x0beq ok_nand_readbad_nand_read:loop2:b loop2ok_nand_read:mov r0, #0ldr r1, =TEXT_BASEmov r2, #0x400go_next:ldr r3, [r0], #4ldr r4, [r1], #4teq r3, r4bne notmatchsubs r2, r2, #4beq stack_setupbne go_nextnotmatch:loop3:b loop3nor_boot:adr r0, _start /* r0 <- current position of code */ldr r1, _TEXT_BASE /* test if we run from flash or RAM */ldr r2, _armboot_startldr r3, _bss_startsub r2, r3, r2 /* r2 <- size of armboot */add r2, r0, r2 /* r2 <- source end address */copy_loop:ldmia r0!, {r3-r10} /* copy from source address [r0] */stmia r1!, {r3-r10} /* copy to target address [r1] */cmp r0, r2 /* until source end addreee [r2] */ble copy_loop/* Set up the stack */接着在board/mao2440下新建文件nand_boot.c,代码如下。
u-boot nandflash 烧写
u-boot中NAND Flash操作及烧写芯片内部存储布局及存储操作特点: K9F1208 64MB一片Nand flash为一个设备(device), 其数据存储分层为:1 (Device) = 4096 (Blocks)1 (Block) -= 32 (Pages/Rows) 页与行是相同的意思,叫法不一样1 (Page) = 528 (Bytes) = 数据块大小(512Bytes) + OOB 块大小(16Bytes)根据芯片规格,计算需要擦除的块大小,按上述指令操作,可以升级u-boot。
NAND操作指令指令 Flash内地址擦除长度nand erase 0x100000 0x200000指令内存中地址 Flash内地址写入长度nand write 0x20000000 0x100000 0x200000nand read 0x20000000 0x100000 0x200000注意写入长度的计算,要以块为单位,计算字节数量。
如:u-boot.bin大小为:114028 bytes一个NAND数据块的容量:32 ×512 = 16384 bytes需要擦除NAND数据块的数量:114028÷16384 = 6.96 ≈ 7 (往上取整)转为擦除NAND数据的长度:16384 ×7 = 114688 bytes (十进制)=0x1_C000 写入以页page为单位,一页大小为512B如何升级bootloader(以下以烧写u-boot.bin为实例)1、用tftp下载u-boot.bin到内存,如0x3300_0000tftp 33000000 u-boot.bin2、擦除uboot分区1Mnand erase 0x0 0x00100000 // 1M的bootloader分区3、写入NAND中nand write 0x33000000 0x0 0x1C000 // u-boot.bin实际大小需用的block (注:如果板子为空白,用sjf2410工具,通过并口,烧写到NAND的0x0地址即可)如何烧写linux kernel1、 tftp下载uImage2.6.14到内存,如0x3400_0000tftp 34000000 uImage2.6.142、擦除kernel分区3Mnand erase 00100000 003000003、写入NAND中nand write 34000000 00100000 00300000如何烧写文件系统fs4、 tftp下载rootfs.cramfs到内存,如0x3400_0000tftp 34000000 rootfs.cramfs5、擦除root分区40Mnand erase 00400000 028000006、写入NAND中nand write 34000000 00400000 (实际大小,往前取512B的整数倍)奇怪,3400_0000已经是64M内存的上限边界,为什么居然还可以使用??2011-5-17 yang 好像3400_0000已经自动映射到 3000_0000的。
Nand Flash 启动过程详解
Nand Flash 启动如果内部没有ROM或ROM没有专门的bootloarder, 应该是只能从nor flash启动。
三星的2410可以从NF启动程序,它会把第一块的前4KB复制到内部SRAM中然后从SRAM 执行,也就是说,你需要编写一个长度小于4K的引导程序,作用是将主程序拷贝到SDRAM中运行(NF地址不是线性的,程序不能直接运行,必须拷贝到线性RAM中). NOR FLASH地址线和数据线分开,来了地址和控制信号,数据就出来。
NAND Flash地址线和数据线在一起,需要用程序来控制,才能出数据。
通俗的说,就是光给地址不行,要先命令,再给地址,才能读到NAND的数据。
而且都是在一个总线完成的。
nboot是从NAND flash读image到内存并执行,eboot是从以太网(用tftp)下载image到内存并执行。
将nboot.nb0烧到第0块,将eboot.nb0烧到第2块。
启动时nboot从flash读出eboot并执行之,如何就可以通过pb把nk.bin下载到目标板上执行了,开发机和目标板可以用交叉线直接连接。
nboot是nand flash bootloader的意思。
S3C2410可以直接从nand flash 启动,但是不能超过4k。
nboot是系统启动后最先执行的代码,它有两种,一种是跳转到eboot;一种是跳转到nk。
主要的功能其实是在eboot里。
随着消费类电子产品包括PDA ,MP3 、智能手机等手持设备的市场需求逐步扩大,产品间的竞争也愈发激烈,降低产品的设计成本,提升产品的市场竞争力成为嵌入式系统开发者所面临的重大挑战。
NAND FLASH 和NORFLASH 作为两种主要的非易失性存储器,被应用于各种嵌入式系统。
其中NAND FLASH 主要优点在于存储密度高、容量大,有更占优势的存储性价比。
但是NANDFLASH 由于其独特的页式读写方式,并不适合程序的直接执行。