分享

NAND FLASH 和 NOR FLASH开机启动

 happy123god 2012-08-23

1.什么是nand,什么是nor?nand启动与nor启动的区别及联系。(收集整理)
  
  答:1)在NOR FLASH里面可以直接执行代码,而在NAND FLASH里面不可以,在2410里面,如果选择NAND启动方式的话,NAND里的代码是被拷贝到RAM里面去执行的。
  
  2)NAND FLASH每次取数据前要写入好像是0X55,0XAA才行,而NOR FLASH直接取到数据。
  
  3)NOR FLASH地址线和数据线分开,来了地址和控制信号,数据就出来。NAND Flash地址线和数据线在一起,需要用程序来控制,才能出数据。
  
  2.如何开机进入BIOS模式?
  
  答:将S2跳转开关打至nor flash端即可。
  
  3.使用supervivi作为bootloader.而有些开发板使用uboot
  
  4.SEC s3c241x什么意思?
  
  三、ARM的nor flash与nand flash启动过程区别
  
  s3c2440启动过程详解
  1:地址空间的分配
  2:开发板上一般都用SDRAM做内存flash(nor、nand)来当做ROM。其中nand flash没有地址线,一次至少要读一页(512B).其他两个有地址线
  3:nandflash不用来运行代码,只用来存储代码,NORflash,SDRAM可以直接运行代码)
  4:s3c2440总共有8个内存banks
   6个内存bank可以当作ROM或者SRAM来使用
   留下的2个bank除了当作ROM 或者SRAM,还可以用SDRAM(各种内存的读写方式不一样)
   7个bank的起始地址是固定的
   还有一个灵活的bank的内存地址,并且bank大小也可以改变
  5:s3c2440支持两种启动模式:NAND和非NAND(这里是nor flash)。
  具体采用的方式取决于OM0、OM1两个引脚
  OM[1:0]所决定的启动方式
  OM[1:0]=00时,处理器从NAND Flash启动
  OM[1:0]=01时,处理器从16位宽度的ROM启动
  OM[1:0]=10时,处理器从32位宽度的ROM启动。
  OM[1:0]=11时,处理器从Test Mode启动。
  
  6.开发板出厂时已经在nand flash,nor flash烧入了相同的BIOS。
  
  
  
  
  当从NAND启动时
  
   cpu会自动从NAND flash中读取前4KB的数据放置在片内SRAM里(s3c2440是soc),同时把这段片内SRAM映射到nGCS0片选的空间(即0x00000000)。cpu是从0x00000000开始执行,也就是NAND flash里的前4KB内容。因为NAND FLASH连地址线都没有,不能直接把NAND映射到0x00000000,只好使用片内SRAM做一个载体。通过这个载体把nandflash中大代码复制到RAM(一般是SDRAM)中去执行
  
  
  当从非NAND flash启动时
  
   nor flash被映射到0x00000000地址(就是nGCS0,这里就不需要片内SRAM来辅助了,所以片内SRAM的起始地址还是0x40000000). 然后cpu从0x00000000开始执行(也就是在Norfalsh中执行)。


nand flash启动   (转自:http://blog.csdn.net/martree/article/details/3313932

2410支持从nand flash启动。通过将flash中最开始的4k代码拷贝到,2410片内的一块不用初始化的sram中运行,该拷贝过程完全由硬件支持,无需软件操作。

  Nand Flash控制器有一个特殊的功能,在S3C2410上电后,Nand Flash控制器会自动的把Nand Flash上的前4K数据搬移到4K内部RAM中,并把0x00000000设置内部RAM的起始地址,CPU从内部RAM的0x00000000位置开始启动。这个过程不需要程序干涉。程序员需要完成的工作,是把最核心的启动程序放在Nand Flash的前4K中。
    u-boot源码不支持从nand flash启动,可是s3c2410支持从nand flash启动,开发板(sbc-2410x)加电后s3c2410将nand flash的前4k(保存有u-boot的部分功能--拷贝功能--把nand flash中的内容拷贝到SDRAM)拷贝到sram(s3c2410芯片内的sram),即nGCS0,映射地址为0x00000000。这就需要修改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无法取得可以执行的代码,也就不能初始化系统了。例如:str r0,[r1],该命令包含了目的地址和源寄存器,按照cpu的时序将寄存器中的数据保存到内存中。如果想把数据存储到nand flash上,必须按照nand flash的读写时序进行,如先写NFCONF,然后NFCMD,NFADDR,最后读NFDATA。
2.
NAND FLASH是顺序存取设备,不能够被随机访问,读写访问以页为单位,程序就不能够分支或跳转,这样你如何去设计程序。

bootloader
bootloader是芯片复位后进入操作系统之前执行的一段代码,完成由硬件启动到操作系统启动的过渡,为运行操作系统提供基本的运行环境,如初始化CPU、堆栈、初始化存储器系统等,其功能类似于PC机的BIOS。




从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闪存的命令操作。
  
开发环境
本设计中目标板硬件环境如下:CPU为S3C2410,SDRAM为HY57V561620,NAND闪存为64MB的K9F1208U0A。
主机软件环境为Redhat9.0、 u-boot-1.1.3、gcc 2.95.3。修改U-BOOT的Makefile,加入:
wch2410_config : unconfig
@./mkconfig $(@:_config=) arm arm920t wch2410 NULL s3c24x0
即将开发板起名为wch2410,接下来依次进行如下操作:
mkdir board/wch2410
cp board/smdk2410 board/wch2410
mv smdk2410.c wch2410.c
cp include/configs/smdk2410.h include/configs/wch2410.h
export PATH=/usr/local/arm/2.95.3/bin:$PATH
最后执行:
make wch2410_config
make all ARCH=arm
生成u-boot.bin,即通过了测试编译。
具体设计
支持NAND闪存的启动程序设计
因为U-BOOT的入口程序是/cpu/arm920t/start.S,故需在该程序中添加NAND闪存的复位程序,以及实现从NAND闪存中把U-BOOT搬移到RAM中的功能程序。
首先在/include/configs/wch2410.h中加入CONFIG_S3C2410_NAND_BOOT, 如下:
#define CONFIG_S3C2410_NAND_BOOT 1      @支持从NAND 闪存中启动
然后在/cpu/arm920t/start.S中添加
#ifdef CONFIG_S3C2410_NAND_BOOT
copy_myself:
mov r10, lr
ldr sp, DW_STACK_START         @安装栈的起始地址
mov fp, #0                     @初始化帧指针寄存器
bl nand_reset                  @跳到复位C函数去执行,执行NAND闪存复位
.......
/*从NAND闪存中把U-BOOT拷贝到RAM*/
ldr r0, =UBOOT_RAM_BASE        @ 设置第1个参数: UBOOT在RAM中的起始地址
mov r1, #0x0                   @ 设置第2个参数:NAND闪存的起始地址
mov r2, #0x20000               @ 设置第3个参数: U-BOOT的长度(128KB)
bl nand_read_whole             @ 调用nand_read_whole(),把NAND闪存中的数据读入到RAM中
tst r0, #0x0                   @ 如果函数的返回值为0,表示执行成功
beq ok_nand_read                @ 执行内存比较,把RAM中的前4K内容与NAND闪存中的前4K内容进行比较, 如果完全相同, 则表示搬移成功
       其中,nand_reset (),nand_read_whole()被加在/board/wch2410/wch2410.c中。
支持U-BOOT命令设计
       在U-BOOT下对nand闪存的支持主要是在命令行下实现对nand闪存的操作。对nand闪存实现的命令为:nand info(打印nand Flash信息)、nand device(显示某个nand闪存设备)、nand read(读取nand闪存)、nand write(写nand闪存)、nand erease(擦除nand闪存)、nand bad(显示坏块)等。
       用到的主要数据结构有:struct nand_flash_dev、struct nand_chip。前者包括主要的芯片型号、存储容量、设备ID、I/O总线宽度等信息;后者是具体对NAND闪存进行操作时用到的信息。
a. 设置配置选项
       修改/include/configs/wch2410.h,主要是在CONFIG_COMMANDS中打开CFG_CMD_NAND选项。定义NAND闪存控制器在SFR区中的起始寄存器地址、页面大小,定义NAND闪存命令层的底层接口函数等。
b. 加入NAND闪存芯片型号
       在/include/linux/mtd/ nand_ids.h中对如下结构体赋值进行修改:
static struct nand_flash_dev nand_flash_ids[] = {
......
{"Samsung K9F1208U0A", NAND_MFR_SAMSUNG, 0x76, 26, 0, 3, 0x4000, 0},
.......
                                                 }
这样对于该款NAND闪存芯片的操作才能正确执行。
c. 编写NAND闪存初始化函数
在/board/wch2410/wch2410.c中加入nand_init()函数。
void nand_init(void)
{
/* 初始化NAND闪存控制器, 以及NAND闪存芯片 */
nand_reset();
/* 调用nand_probe()来检测芯片类型 */
printf ("%4lu MB/n", nand_probe(CFG_NAND_BASE) >> 20);
}
该函数在启动时被start_armboot()调用。
   
       最后重新编译U-BOOT并将生成的u-boot.bin烧入NAND闪存中,目标板上电后从串口输出如下信息:
U-Boot 1.1.3 (Nov 14 2006 - 11:29:50)
U-Boot code: 33F80000 -> 33F9C9E4     BSS: -> 33FA0B28
RAM Configuration:
Bank #0: 30000000 64 MB
## Unknown Flash on Bank 0: ID 0xffff, Size = 0x00000000 = 0 MB
Flash:     0 kB
NAND:     64 MB
In:       serial
Out:      serial
Err:      serial
Hit any key to stop autoboot:     0
wch2410 #
结语
      
以往将U-BOOT移植到ARM9平台中的解决方案主要针对的是ARM9中的NOR闪存,因为NOR闪存的结构特点致使应用程序可以直接在其内部运行,不用把代码读到RAM中,移植过程相对简单。从NAND闪存中启动U-BOOT的设计难点在于NAND闪存需要把U-BOOT的代码搬移到RAM中,并要让U-BOOT支持NAND闪存的命令操作。本文介绍了实现这一设计的思路及具体程序。移植后,U-BOOT在嵌入式系统中运行良好。
参考文献
1 杜春雷 . ARM 体系结构与编程 [M]. 北京 : 清华大学出版社, 2003
2 S3C2410 User's Mannual[Z].Samsung

vivi中的源码
#ifdef CONFIG_S3C2410_NAND_BOOT
@
@ copy_myself: copy vivi to ram
@
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, =VIVI_RAM_BASE
    mov     r1, #0x0
    mov    r2, #0x20000
    bl    nand_read_ll

    tst    r0, #0x0
    beq    ok_nand_read

 
Freescale i.MX25系列arm的NAND Flash启动过程
 
Freescale的i.MX25系列arm支持的启动方式有: WEIM接口的NOR Flash, SLC/MLC NAND Flash(通过NFC), SD/MMC卡, EEPROM通过SPI接口, EEPROM通过I2C接口, USB启动等.

不同的启动方式可以通过arm内部寄存器, 芯片引脚, 特别的i.MX25中有eFuse部分来进行相应的配置. 这里总结下基于NAND Flash的启动方式.

涉及到NAND Flash配置的eFuse有:(部分)



--------------------------
bootloader, NAND Flash和boot ROM

在我接触的系统中, 系统的bootloader程序以及application程序都是储存在NAND Flash中的, 系统上电之后首先要将bootloader加载进内存中, 然后bootloader再将application加载进内存里. 所谓"加载"主要指的是程序的拷贝, 其他还有堆栈, 中断向量表, 程序指针等一些初始化操作.

上述过程中第一步, 即"上电后, 将bootloader加载进内存"是怎么完成的呢? 毕竟上电之后内存里面什么程序都没有, 而bootloader又是在NAND flash里的, 就是说系统怎么知道如何读取bootloader呢?(没有flash驱动程序)

这就要结合上面那张表来回答, 这些表里的设置是给谁看的呢? 是给芯片内部的boot ROM来看的, 这个boot ROM是真正系统启动的第一步, 它完成了硬件的初始化, 下一步要读取镜像的验证和读取(这里是bootloader), 然后将程序指针跳到相应的地址上去执行, 它里面含有flash的驱动程序.

详细的内部boot ROM读取NAND Flash的过程, 原文叙述如下:


ARM中的一些地址映射为:



--------------------------
从boot ROM到bootloader

这样应该能理解boot ROM是怎么将bootloader从NAND Flash中加载进内存的吧, 这里的内存是SDRAM, 地址为0x80000000. 上面一段原文中提到了一个词是"image header", 根据它, boot ROM知道读取多少数据, 并且程序指针如何跳转. 这个过程可以用下图表示:


图中的IPL(initial program loader)应该就是bootloader吧. 另外一些术语的解释为:


一个典型的flash image header文件的原文解释为:


bootloader的工程编译链接好之后通常是bin或者hex文件, 然后通过某种hex或bin工具把上述那个头文件加进去. 然后再烧到NAND Flash中去.

--------------------------
从bootloader到application

我的系统里, flash最开始的一个block用于储存bootloader, 其余的被格式化成一个类似fat16的分区, 所以对于Flash的读写都是基于文件的操作. Application就是分区上的一个文件.

bootloader要将这个application加载进内存, 所以bootloader里面有操作系统和文件系统, 能够对文件进行读写. 用的是threadX操作系统和fileX文件系统, 都很小, bootloader大概88KB.


 
 
ARM的固件和bootloader,STANDSTONE固件程序,OS初始化
 
 
Firmware和bootloader
固件程序通常驻留在ROM中的,当系统上电时,首先执行的程序。
一个主要的目的就是加载和启动操作系统,这部分程序叫为bootloader。

固件程序执行流程为:



系统的image储存在某种媒介上,通常是有文件系统(file system)的,这时候image就是一个文件,操作文件需要固件程序能够支持文件系统。

image也可能就是一组二进制代码,这是最原始的image。通常ARM的image是ELF格式的,带有头和调试信息,ELF是老的COFF格式的升级版。

----------------------------
STANDSTONE固件程序
书中介绍了一个叫STANDSTONE的简单固件程序,它完成设置系统环境、加载并启动镜像的功能。所谓启动就是将系统的控制权交给启动了镜像,也就是一般所说的操作系统了。

STANDSTONE的程序工程组织结构:


可以看出,编译好的STANDSTONE产生的image,不仅包含了STANDSTONE的代码,而且包含了payload的bin(二进制镜像)。

STANDSTONE的执行流程:



ARM上电时从0x00000000开始执行,向量表就放在该地址,上电即进入reset异常。
STANDSTONE较简单,没用其他异常,将其都设置成循环。
reset异常,处理器跳转到standstone_init1的地方去运行,接着是第二步:


这一步在设置系统寄存器里特定的LED显示寄存器。




这部分代码完成了内存重映射,本来flash ROM是0地址开始的,现在编程了0x01800000,当然物理上是没变,只是逻辑上改了。
并且初始化了两个bank的SRAM。
分析代码:第一句是重映射钱standstone_init2的绝对地址,standstone_init2是固件程序的一部分,所以肯定在0x0到0x00080000范围内,重映射好要接着执行standstone_init2,所以需要计算出重映射好之后的地址,这也就是第三句指令做的事。
然后r0指向memorymaptable_str,然后将memorymaptable_str开始的数据赋值给r1-r12,r0后又赋值为某个系统寄存器,将r1-r12的数据赋值给这个系统寄存器。这几句就完成了重映射,这个系统寄存器是内存控制器,它接受memorymaptable_str这类能够表明内存分布情况的数据。
最后跳转到standstone_init2,这句很巧妙,因为STMIA执行好之后,PC所指向的地址已经变成了SDRAM里的内容,指令都到0x01800000以后的地址去了,但因为ARM的流水线结构,所以MOV语句已经取出,能够执行。

第四步是初始化串口,用于交互式命令通信,由于这部分和特定芯片相关,没有显示。

第五步是bootloader,就是加载之前那个payload的bin镜像


程序里假设了payload的bin镜像的起始地址payload_start_address,结束地址payload_end_address。
一开始将r12设为payload_start_address,将r13设为0地址,也就是SRAM的地址。
将payload_start_address拷贝至{r0-r11}再由{r0-r11}拷贝至SRAM。
拷贝完成之后跳转到0地址去执行payload的bin镜像。

----------------------------
OS初始化
通过前一篇的固件程序,假设已经完成了内存重映射和OS的拷贝,即将SRAM映射到了0x00000000和0x00080000之间,OS从固件拷贝到了0x00000000起始的地方。另外,假设芯片内的配置寄存器的基地址是0x03ff0000。

看一下OS的工程结构:

e7t/devices文件夹包含驱动程序,events下包含异常中断服务程序,core下是OS,apps下是应用程序。

虽然固件程序里完成了初始化,但那个初始化是针对芯片而言的,比如设置栈指针、内存映射、OS拷贝等;OS拷贝到0x0地址之后,固件程序将pc也置为了0x0,要知道0x0地址是存放异常向量表的,第一个是reset异常,这也就是这个OS的第一条指令了,这不过这个reset指的是OS的reset,和固件程序里的reset不是同一个。

OS的reset异常完成OS的初始化工作,分成三个阶段:栈设置、PCB设置、开始C程序。首先是栈设置:

bringupInitFIQRegisters程序段将FIQ的寄存器用作系统状态记录,所以对FIQ的三个寄存器做了设为了0。接下去的几句是设置了栈指针,因为使在SVC模式下,所以可以通过MSR指令改写模式,并设置该模式下的栈。设置好之后,再回到SVC模式。

PCB设置:PSB指的是process control blcok,是一种数据结构,当任务发生切换时,将PCB数据拷贝到栈上:



最后跳转至C程序:

进入C程序后,第一个函数初始化了驱动、IO、异常中断服务等;然后开始定时器即时;然后切换至user模式;然后开始第一个任务。


系统内存分布


----------------------------
reference:ARM System Developer's Guide.pdf

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多